【Java】——自定义注解对参数进行校验、spring扫描自定义注解
原创
©著作权归作者所有:来自51CTO博客作者mb62e351395277e的原创作品,请联系作者获取转载授权,否则将追究法律责任
前提
上篇博客中详细介绍自定义注解的使用,本文主要是对自定义注解的进一步深入。会使用CGLIb进行动态代理来完成对方法参数是否为空的判断,以及再spring中如何扫描自定义注解
自定义注解对方法参数为空校验
为什么要用动态代理?
因为Java的反射拿不到参数的相关信息,对方法参数进行校验,肯定是要在方法执行前进行校验,所以就需要动态代理来完成。对真实的对象进行代理,让代理对象执行参数校验这一部分的操作。
1、自定义注解
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String msg() default "参数不能为空";
}
2、代理类以及校验方法
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class HelloServiceCgLib implements MethodInterceptor {
private Class target;
public Object getInstance(Class target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("我是CGLIB的动态代理");
System.out.println("我准备执行了");
if (!check(method,objects)) {
System.out.println("我没能成功执行");
return false;
}
Object returnObj = proxy.invokeSuper(object, objects);
System.out.println("我已经正确执行过了");
return returnObj;
}
/**
* 对参数校验的方法
* @param method 目标方法
* @param objects 相关参数值
* @return
*/
public boolean check(Method method,Object[] objects) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i <parameters.length; i++) {
Parameter parameter = parameters[i];
if (parameter.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = parameter.getAnnotation(MyAnnotation.class);
if (objects==null ||objects[i]==null) {
System.out.println(parameter.getName()+annotation.msg());
return false;
}
}
}
return true;
}
}
3、真实类
public class Hello {
public void sayHello(@MyAnnotation String name,@MyAnnotation String start) {
System.out.println("hello "+name);
System.out.println(start);
}
}
4、调用过程
public class AnnotationTest {
public static void main(String[] args) {
HelloServiceCgLib helloServiceCgLib = new HelloServiceCgLib();
Hello proxy = (Hello) helloServiceCgLib.getInstance(Hello.class);
proxy.sayHello("world",null);
}
5、执行结果
对第二个参数进行拦截,判断为空,阻止方法的非正常执行。
spring扫描自定义注解
在使用Spring的时候需要自定义annotation来满足项目需求。
在Bean初始化的过程都会调用BeanPostProcessor接口即Spring中的后置处理器,这个接口是Spring IOC容器给我们提供扩展接口,方便在Spring容器中完成bean实例化,配置以及其他初始化方法前后添加一些自己处理的逻辑。
postProcessAfterInitialization方法,所以可以利用自定义这个方法,达到让spring扫描自定义注解的目的。
1、自定义注解
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyListener {
String value() default "spring";
}
2、配置spring扫描
@Component
public class MyListenerProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
if (methods != null) {
for (Method method : methods) {
MyListener myListener = AnnotationUtils.findAnnotation(method, MyListener.class);
// process
if (myListener != null) {
System.out.println(method.getName());
System.out.println(myListener.value());
}
}
}
return bean;
}
}
3、配置要扫描的方法
@Service
public class MyService {
@MyListener
public void onMessage() {
System.out.println("我被调用了");
}
}
4、初始化,判断spring是否正确扫描注解
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
MyService bean = ac.getBean(MyService.class);}
}
5、注解被spring扫描到了
总结
通过对自定义注解的使用可以很好加深对动态代理这些概念的认识,对spring框架的理解同样可以更进一步。