一个Web项目,想做一个默认通用接口的功能。父类BaseController有默认的对外接口(/page,/list,/detail,/update等)以及默认的实现。已经实现,(继承shiro,需要接口访问权限功能)但是shiro的授权(@RequiresPermissions)不能自定义了。想着利用Spring的BeanPostProcessor给ControllerBean的API方法的授权做动态修改。但实际并不能如此只能另寻他法了。
//父类定义默认对外接口,以及方法实现
public class BaseController<T extends BaseService,M extends BaseModel> {
@GetMapping("/page")
@RequiresPermissions(":read")
public Object defaultPage(ModelMap modelMap, @RequestParam Map<String, Object> param) {
service.beforePage(param);
return setSuccessModelMap(modelMap, service.getPage(param,service.pageHandler()));
}
@GetMapping("/list")
@RequiresPermissions(":read")
public Object defaultList(ModelMap modelMap, @RequestParam Map<String, Object> param) {
service.beforeList(param);
return setSuccessModelMap(modelMap, service.getList(param,service.listHandler()));
}
@GetMapping("/detail")
@RequiresPermissions(":read")
public Object defaultDetail(ModelMap modelMap, Long id) {
service.beforeDetail(id);
M t= (M) service.getById(id);
return setSuccessModelMap(modelMap,service.afterDetail(t));
}
@PostMapping("/update")
@RequiresPermissions(":update")
public Object defaultUpdate(ModelMap modelMap, M param) {
if(param==null||param.getId()==null){
service.beforeAdd(param);
M t= (M) service.update(param);
return setSuccessModelMap(modelMap, service.afterAdd(t));
}else{
service.beforeUpdate(param);
M t= (M)service.update(param);
return setSuccessModelMap(modelMap, service.afterUpdate(t));
}
}
@DeleteMapping("/delete")
@RequiresPermissions(":del")
public Object defaultDelete(ModelMap modelMap, Long id) {
return setSuccessModelMap(modelMap, service.delete(id));
}
@DeleteMapping("/del")
@RequiresPermissions(":del")
public Object defaultDel(ModelMap modelMap, Long id) {
return setSuccessModelMap(modelMap, service.del(id));
}
@DeleteMapping("/deletes")
@RequiresPermissions(":del")
public Object defaultDeletes(ModelMap modelMap, String ids) {
service.deletes(ids);
return setSuccessModelMap(modelMap);
}
}
//授权动态修改
@Component
public class DefaultRequiresPermissionPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class clazz=bean.getClass();
MController mapping= (MController) clazz.getAnnotation(MController.class);
if(mapping!=null){
String[] roots=mapping.value();
if(roots.length>0){
String rootUrl= Stream.of(roots).collect(Collectors.joining()).substring(1);
List<String> defaultMethods= Arrays.asList("defaultPage","defaultList","defaultDetail","defaultUpdate","defaultDelete","defaultDel","defaultDeletes");
Stream.of(clazz.getMethods())
.filter(method -> defaultMethods.contains(method.getName()))
.forEach(method -> {
RequiresPermissions pageAnnotation= method.getAnnotation(RequiresPermissions.class);
InvocationHandler pageHandler= Proxy.getInvocationHandler(pageAnnotation);
try {
Field pageField = pageHandler.getClass().getDeclaredField("memberValues");
pageField.setAccessible(true);
Map pageMap= (Map) pageField.get(pageHandler);
String[] pageValues= (String[]) pageMap.get("value");
pageValues[0]=rootUrl+pageValues[0];
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
return bean;
}
}
//自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface MController {
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
//String[] exclude() default {};
}
实验中授权确实修改了,但发现所有的Controller的默认接口的权限都是同一个,意识到其实修改的自始至终都是父类方法中的注解。如是做了以下测试:
//注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Temp {
String value();
}
//父类
public class Animal{
@Temp("animal")
public void eat(){}
}
//子类A
public class Dog extends Animal {
}
//子类B
public class Cat extends Animal {
}
//临时工具方法
//获取对象某方法上某注解的value值
public static <T extends Annotation> String getAnnotationValue(Object obj, Class<T> annotationType, String methodName) throws Exception {
Method method=obj.getClass().getMethod(methodName);
T annotation=method.getAnnotation(annotationType);
InvocationHandler handler=Proxy.getInvocationHandler(annotation);
Field field=handler.getClass().getDeclaredField("memberValues");
field.setAccessible(true);
Map map= (Map) field.get(handler);
Object value=map.get("value");
if(value==null)return null;
return value.toString();
}
//设置对象某方法上某注解的value值
public static <T extends Annotation> void setAnnotationValue(Object obj,Class<T> annotationType,String methodName,String value)throws Exception{
Method method=obj.getClass().getMethod(methodName);
T annotation=method.getAnnotation(annotationType);
InvocationHandler handler=Proxy.getInvocationHandler(annotation);
Field field=handler.getClass().getDeclaredField("memberValues");
field.setAccessible(true);
Map map= (Map) field.get(handler);
map.put("value",value);
}
//测试方法
@Test
public void test(){
Dog dog=new Dog();
Cat cat=new Cat();
System.out.println("dog value:"+getAnnotationValue(dog,Temp.class,"eat"));
System.out.println("cat value:"+getAnnotationValue(cat,Temp.class,"eat"));
setAnnotationValue(dog,Temp.class,"eat","dog");
System.out.println("dog value:"+getAnnotationValue(dog,Temp.class,"eat"));
System.out.println("cat value:"+getAnnotationValue(cat,Temp.class,"eat"));
}
//结果
dog value:animal
cat value:animal
dog value:dog
cat value:dog
//A类重写父类方法
public class Dog extends Animal {
@Override
@Temp("animal")
public void eat() {
super.eat();
}
}
//结果
dog value:animal
cat value:animal
dog value:dog
cat value:animal
总结
父类方法的注解有默认值,该类有多个子类。其中一个子类修改了该注解的值,则所有其他子类该方法的值也发生变化,子类不重写的前提下。已通过自定义注解实现