spring boot 利用切面和反射进行自动关联查询

1.定义注解类:

QueryAuto:

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

/**
 * ClassName: Query <br/>
 * Function: 模块关联查询注解<br/>
    用于返回list等方法上
 */
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryAuto {
	
}

 QueryField:

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

/**
 * ClassName: QueryField <br/>
 * Function: 模块关联查询注解<br/>
 * <p>比如字典数据,我们表里面都存的字典id,之前的开发模式是在业务模块的sql里各种left join去查询字典名称.</p> 
 * <p>现在的做法是在实体对象的字典id属性上加上此注解,并写上字典名称属性,会自动去查询数据库,赋值于字典名称属性上去.</p> 

 */
@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryField {
	
	/**
	 * module:(业务模块编码,默认为Dic 字典模块,其他模块的话需要遵循一定的命名规则,
	 * 			详见com.talkweb.query.common.QueryConstant 模块定义常量). <br/>
	 * @author gavin
	 * @return
	 */
	public String module() default "Dic";
	
	/**
	 * nameField:(字典数据名称属性字段). <br/>
	 * @author gavin
	 * @return
	 */
	public String nameField();
}

2.定义自动查询的属性实体类

import java.io.Serializable;

/**
 * ClassName:QueryFieldEntity <br/>
 * Function: 需要自动查询的字段属性对象. <br/>
 */
public class QueryFieldEntity implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private String idFieldName;		//id属性的字段名称
	private String nameFieldName;	//name属性字段名称
	private String module;			//需要查询的模块名称
	
	public String getIdFieldName() {
		return idFieldName;
	}
	public void setIdFieldName(String idFieldName) {
		this.idFieldName = idFieldName;
	}
	public String getNameFieldName() {
		return nameFieldName;
	}
	public void setNameFieldName(String nameFieldName) {
		this.nameFieldName = nameFieldName;
	}
	public String getModule() {
		return module;
	}
	public void setModule(String module) {
		this.module = module;
	}
}

3.定义查询工具类:用于获取需要查询的字段

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.annotation.QueryField;
import com.query.entity.QueryFieldEntity;

/**
 * ClassName:QueryUtil <br/>
 * Function: 自动查询工具类. <br/>
 */
public class QueryUtil {
	
	/**
	 * getQueryAutoField:(得到对象中需要自动查询的字段). <br/>
	 * @author gavin
	 * @param object
	 * @return
	 */
	public static List<QueryFieldEntity> getQueryAutoField(Object object){
		List<QueryFieldEntity> list = new ArrayList<QueryFieldEntity>();
		Class<?> cls = object.getClass();
	    Field[] fields = cls.getDeclaredFields();  
		List<Field> fieldList = new ArrayList<Field>(Arrays.asList(fields));
	    //获取父类字段
	    Class<?> supercls = cls.getSuperclass();
	    Field[] superfields = supercls.getDeclaredFields();  
	   
	    fieldList.addAll( new ArrayList<Field>(Arrays.asList(superfields)));
	    for (Field field : fieldList) {
	    	field.setAccessible(true);
	    	QueryField queryField = field.getAnnotation(QueryField.class);
	    	if(queryField != null){
	    		QueryFieldEntity entity = new QueryFieldEntity();
	    		entity.setIdFieldName(field.getName());
	    		entity.setNameFieldName(queryField.nameField());
	    		entity.setModule(queryField.module());
	    		list.add(entity);
	    	}
	    }
		return list;
	}
	
	/**
	 * getQueryAutoField:(得到对象中需要自动查询的字段). <br/>
	 * @author gavin
	 * @param object
	 * @return
	 */
	public static List<QueryFieldEntity> getSuperQueryAutoField(Object object){
		List<QueryFieldEntity> list = new ArrayList<QueryFieldEntity>();
		Class<?> supercls = object.getClass().getSuperclass();
	    Field[] fields = supercls.getDeclaredFields();  
	    for (Field field : fields) {
	    	field.setAccessible(true);
	    	QueryField queryField = field.getAnnotation(QueryField.class);
	    	if(queryField != null){
	    		QueryFieldEntity entity = new QueryFieldEntity();
	    		entity.setIdFieldName(field.getName());
	    		entity.setNameFieldName(queryField.nameField());
	    		entity.setModule(queryField.module());
	    		list.add(entity);
	    	}
	    }
		return list;
	}
}

4.定义查询接口:用于处理不同类型的数据

/**
 * ClassName:Query <br/>
 * Function: TODO ADD FUNCTION. <br/>
 * Date:     2017年6月27日 上午9:47:04 <br/>

 */
public interface Query {
	/**
	 * doPage:(处理分页数据). <br/>
	 * @author gavin
	 * @param object
	 */
	public void doPage(Object object);
	
	/**
	 * doList:(处理简单列表数据). <br/>
	 * @author gavin
	 * @param object
	 */
	public void doList(Object object);
	
	/**
	 * doObject:(处理简单实体对象数据). <br/>
	 * @author gavin
	 * @param object
	 */
	public void doObject(Object object);
}

 

package com.query.query.impl;

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

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.common.Page;
import com.query.adapter.QueryAdapter;
import com.query.common.QueryUtil;
import com.query.entity.QueryFieldEntity;
import com.query.query.Query;

/**
 * ClassName:QueryImpl <br/>
 * Function: 自动查询实现类. <br/>
 * Date:     2017年6月27日 上午9:54:02 <br/>
 * @author   gavin
 * @since    JDK 1.6
 */
@Component
public class QueryImpl implements Query {
	@Resource(name="queryAdapter")
	private QueryAdapter queryAdapter;
	
	@Override
	public void doPage(Object object) {
		Page page = (Page)object;
		try {
			Method m = page.getClass().getMethod("getContent");
			List list = (List)m.invoke(page);
	    	if(list != null && list.size() > 0){
	    		Object o = list.get(0);
				List<QueryFieldEntity> fieldList = QueryUtil.getQueryAutoField(o);
				if(fieldList != null && fieldList.size() > 0){
					for(Object ob : list){
						for(QueryFieldEntity field : fieldList){
							queryAdapter.handle(ob, field);
						}
					}
				}
	    	}
		} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void doList(Object object) {
		List list = (List)object;
		if(list != null && list.size() > 0){
			Object o = list.get(0);
			List<QueryFieldEntity> fieldList = QueryUtil.getQueryAutoField(o);
			if(fieldList != null && fieldList.size() > 0){
				for(Object ob : list){
					for(QueryFieldEntity field : fieldList){
						queryAdapter.handle(ob, field);
					}
				}
			}
		}
	}

	@Override
	public void doObject(Object object) {
		List<QueryFieldEntity> fieldList = QueryUtil.getQueryAutoField(object);
		if(fieldList != null && fieldList.size() > 0){
			for(QueryFieldEntity field : fieldList){
				queryAdapter.handle(object, field);
			}
		}
	}
}

  

5.定义查询适配器:用于使用不同的模块(关联表)

package com.talkweb.query.adapter;

import java.lang.reflect.Field;

import com.config.ApplicationContextRegister;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;

import com.query.entity.QueryFieldEntity;
import com.query.query.QueryModule;

/**
 * ClassName:QueryAdapter <br/>
 * Function: 自动查询适配器. <br/>
 */
@Component("queryAdapter")
public class QueryAdapter {
	
	/**
	 * handle:(根据不同模块,适配不同的接口实现类处理查询). <br/>
	 * @author gavin
	 * @param object
	 * @param field
	 */
	public void handle(Object object,QueryFieldEntity field) {
		if(field.getModule() != null && !"".equals(field.getModule())){
			//这里必须注意,关于字段上的配置的Module属性,必须要和QueryModule接口实现类的后缀一致,
			//这里getBean的bean的名称就是queryModuleOf + Module属性!!!!
			QueryModule queryModule = (QueryModule) ApplicationContextRegister.getApplicationContext()
													.getBean("queryModuleOf"+field.getModule());
			if(queryModule != null){
				try {
					Field idField ;
					try{
						idField = object.getClass().getDeclaredField(field.getIdFieldName());
					} catch(NoSuchFieldException e){
						idField = object.getClass().getSuperclass().getDeclaredField(field.getIdFieldName());
					}
					idField.setAccessible(true);
					String id = (String)idField.get(object);
					if(id != null && !"".equals(id)){
						String name = queryModule.query(id,object, field);
						if(name != null ){	//这里只判断null,不做空判断,因为空可能是数据本身值
							Field nameField ;
							try{
								nameField =  object.getClass().getDeclaredField(field.getNameFieldName());
							} catch(NoSuchFieldException e){
								nameField = object.getClass().getSuperclass().getDeclaredField(field.getNameFieldName());
							}
							nameField.setAccessible(true);
							nameField.set(object, name);
						}
					}
				} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

  

6.定义模块处理类

package com.query.query;

import com.query.entity.QueryFieldEntity;

/**
 * ClassName:QueryModule <br/>
 * Function: 查询具体模块字段接口. <br/>
 * Date:     2017年6月27日 上午11:30:00 <br/>
 * @author   gavin
 * @since    JDK 1.6
 */
public interface QueryModule {
	
	/**
	 * query:(查询具体模块方法,可以直接拿到id去做相应查询,然后返回). <br/>
	 * @author gavin
	 * @param id		id属性的值
	 * @param object	具体的实体对象
	 * @param field		反射需要用到的相关属性
	 * @return
	 */
	public String query(String id, Object object, QueryFieldEntity field);
}

  

package com.query.query.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.entity.pub.Dic;
import com.query.entity.QueryFieldEntity;
import com.query.query.QueryModule;
import com.service.pub.impl.DicService;

/**
 * ClassName:QueryModuleOfDic <br/>
 * Function: 字典模块查询服务. <br/>

 */
@Component("queryModuleOfDic") //用于适配器中上下文获取bean
public class QueryModuleOfDic implements QueryModule {
	@Autowired
	private DicService dicService;
	
	@Override
	public String query(String id, Object object, QueryFieldEntity fieldEntity) {
		String name = null;
		if(id != null && !"".equals(id)){
			Dic dic = dicService.get(id);	//根据id查询字典值
			if(dic != null){
				name = dic.getName();
			}
		}
		return name;
	}
}

  

7.定义查询切面类  

 

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.annotation.QueryAuto;
import com.query.query.Query;

/**
 * ClassName:QueryAop <br/>
 * Function: 自动查询切面类. <br/>
 */
@Aspect
@Component
public class QueryAop {
	@Autowired
	private Query query;
	
	private static final Logger LOG = Logger.getLogger(QueryAop.class);
	
	//execution为执行的意思,*代表任意返回值,然后是包名,.*意思是包下面的所有子包 *(..)代表各种方法.  &&  @annotation(queryAuto) 表示并且方法上有为queryAuto的注解
	@Pointcut(value="execution(* com.service..*.*(..)) && @annotation(queryAuto) " , argNames="queryAuto")
    private void queryAuto(QueryAuto queryAuto){}//定义流程切入点 
	
	/**
	 * queryAuto:(自动查询切面方法). <br/>
	 * @author gavin
	 * @param point
	 * @param queryAuto
	 * @return
	 * @throws Throwable
	 */
	@Around(value = "queryAuto(queryAuto)" , argNames="queryAuto")
    public Object queryAuto(ProceedingJoinPoint point,QueryAuto queryAuto) throws Throwable {
		long start = System.currentTimeMillis();
        System.out.println("QueryAuto--->进入自动查询AOP");
        LOG.info("QueryAuto--->进入自动查询AOP");
        
        Object object = point.proceed();//执行方法
        if(object != null){
        	if(object instanceof com.talkweb.common.Page){	//返回类型为page对象
        		query.doPage(object);
        	} else if(object instanceof java.util.List){	//返回类型为List
        		query.doList(object);
        	} else if(object instanceof java.util.Map){		//返回类型为Map
        		//map 不作处理
        	} else if(object.getClass().getName().indexOf("com.talkweb.entity") >=0 ){	//默认为系统业务实体对象,如果为其他的比如:String Integer...基础数据类型,不作处理
        		query.doObject(object);
        	}
        }
        System.out.println("QueryAuto--->退出自动查询AOP方法。方法执行时长: "+ (System.currentTimeMillis() - start) + "ms");
        LOG.info("QueryAuto--->退出自动查询AOP方法。方法执行时长: "+ (System.currentTimeMillis() - start) + "ms");
        return object;
	}
}

  

8.使用

1.在实体类中定义查询字段

@QueryField(module=QueryConstant.MODULE_AssumeComPany,nameField="fill_park_name")
private String fill_park_pid;	//填报所在园区,当前填报人所在的园区
private String fill_park_name;

  

2.在service类中添加自动查询注解

@QueryAuto
	@Override
	public Page<NatureMainEntity> list(int pageNum, int pageSize, Map<String, Object> parameter) {
		PageHelper.startPage(pageNum, pageSize, pageNum == 0 ? false : true);
		List<NatureMainEntity> reuslt = getDao().list(parameter);
		Page<NatureMainEntity> page = new Page<NatureMainEntity>(reuslt);
		return page;
	}

  

 

posted on 2019-02-27 17:13  L丶小布  阅读(228)  评论(0编辑  收藏  举报

导航