在spring中玩转自定义注解


本文教你如何在spring中自定义注解,并像spring原生注解一样启动就生效。

1.注解扫描生效原理

ClassPathScanningCandidateComponentProvider类:这个类的主要作用是进行包扫描,可以指定扫描的包和特定的类,默认扫描路径是类路径,且扫描的是Component注解标记的类或者Componet作为元注解标记的注解标记的类。这个类有两个变量 includeFilters 和 excludeFilters ,前者用来表示扫描中需要过滤包含的条件,后者用来标识扫描过程中过滤排除的条件,且判断逻辑是先判断excludeFilters 排除条件 ,后判断 includeFilters 条件,前面的条件生效则不再判断后面的条件。

ClassPathBeanDefinitionScanner是上一个类的父类,在其基础上做了注册功能,所以ClassPathBeanDefinitionScanner需要传入一个BeanDefinitionRegistry对象.

在 Spring 中可以通过 MetadataReader 获取 ClassMetadata 以及 AnnotationMetadata,然后获取相应元数据。ClassMetadata 可以获取类的各种元数据,比如类名,接口等。
而 AnnotationMetadata 可以获取当前类上注解的元数据,如注解名字,以及元注解信息等。

AnnotationMetadata 存在两个实现类分别为 StandardAnnotationMetadata与 AnnotationMetadataReadingVisitor。

StandardAnnotationMetadata主要使用 Java 反射原理获取元数据。
AnnotationMetadataReadingVisitor 使用 ASM 框架获取元数据。

2.基于@Component元注解开发自定义注解

2.1 开发注解,使用component作为元注解

@Component
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface GBaseMapper {

    String sql();

    int timeout() default 30 ;
}

2.2 实现InstantiationAwareBeanPostProcessor接口,实现自定义创建对象进行注册

@Component
public class GbaseInstatiationBeanPostProcess implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        //获取自定义注解,判断是否是自定义注解标记的类
        GBaseMapper annotation = beanClass.getAnnotation(GBaseMapper.class);
        //非自定义注解标记类则不处理
        if (annotation == null)
            return null;
        Class<?>[] interfaces = beanClass.getInterfaces();
        Object source = null;
        try {
            //创建对象
            source = beanClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        //将对象进行动态代理,返回注册代理对象
        Object pro = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, new MyHandler(source));
        return pro;
    }

    private class  MyHandler implements InvocationHandler{
        //被代理对象
        private Object target;
        //缓存被代理对象中方法与注解的映射
        private ConcurrentHashMap<Method,GBaseMapper> map = new ConcurrentHashMap<Method,GBaseMapper>();

        public MyHandler(Object target) {
            this.target = target;
            Method[] methods = target.getClass().getMethods();
            for (int i = 0 ; i < methods.length;i++){
                if (methods[i].getAnnotation(GBaseMapper.class) != null) {
                    map.putIfAbsent(methods[i], methods[i].getAnnotation(GBaseMapper.class));
                }
            }
        }
		
		//被代理逻辑
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
            //获取被代理对象类上的注解的sql语句
            String sql = target.getClass().getAnnotation(GBaseMapper.class).sql();
            //获取被代理对象中执行的方法对象,参数中传进来的是可能是接口的,被代理对象是实现类
            Method targetMethoed = target.getClass().getMethod(method.getName(), method.getParameterTypes());
            GBaseMapper annotation = map.get(targetMethoed);
            System.out.println("==="+(annotation == null? sql : annotation.sql()));
            return method.invoke(target,objects);
        }
    }
    
}

3.基于注解扫描开发自定义注解

3.1开启功能的自定义注解,标记在启动类上

注意这个类导入了ProxyRequestRegistrar这个类,这个类负责自定义注解 ProxyHttp的扫描和类的注入

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ProxyRequestRegistrar.class)
public @interface EnableHttpRequest {

}

3.2 开发自定义注解,注解在需要注册的类上

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ProxyHttp {

    String url() ;

    int timeout() default 30 ;

    Class callback() default void.class;

}

3.3 注册类开发

负责注解的扫描和自定义类的加载

package com.heron.myproxy;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.util.Map;
import java.util.Set;

/**
 * @Author HeRong
 * @Description 注册bean
 * @Date 2020/9/3 10:49
 * @Version V1.0
 */
public class ProxyRequestRegistrar implements ImportBeanDefinitionRegistrar , ResourceLoaderAware , EnvironmentAware {

    private  ResourceLoader loader;
    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment ;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.loader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        ClassPathScanningCandidateComponentProvider scaner = getScaner();
        scaner.setResourceLoader(loader);
        //添加注解过滤器,只扫描ProxyHttp注解的类
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(ProxyHttp.class);
        scaner.addIncludeFilter(annotationTypeFilter);
        
        //默认扫描主类下的包
        String classPathPackage = ClassUtils.getPackageName(annotationMetadata.getClassName());
        Set<BeanDefinition> candidateComponets = scaner.findCandidateComponents(classPathPackage);
        for (BeanDefinition component : candidateComponets){
            if (component instanceof AnnotatedBeanDefinition){
                //扫描到的注解标记的接口的定义信息
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)component;
                AnnotationMetadata metadata = beanDefinition.getMetadata();
                Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ProxyHttp.class.getCanonicalName());
                Assert.isTrue(metadata.isInterface(),"@ProxyHttp注解只能作用在接口上");
                //构造 HttpReqFactoryBean的定义信息
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HttpReqFactoryBean.class);
                builder.addPropertyValue("type",metadata.getClassName());
                builder.addPropertyValue("url",annotationAttributes.get("url"));
                builder.addPropertyValue("timeout",annotationAttributes.get("timeout"));
                builder.addPropertyValue("callback",annotationAttributes.get("callback"));

//                metadata.getAnnotatedMethods()

                //注册HttpReqFactoryBean
                BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(builder.getBeanDefinition(), metadata.getClassName());
                BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder,beanDefinitionRegistry);
            }
        }
    }

    private ClassPathScanningCandidateComponentProvider getScaner(){
        return  new ClassPathScanningCandidateComponentProvider(false,this.environment){
            //重写判断是否所需的候选组件的方法
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                //拿到元数据,判断是否能够被独立的创建
                if (beanDefinition.getMetadata().isIndependent()) {
                    //判断是否是接口,自定义注解只作用在接口上
                    if (beanDefinition.getMetadata().isInterface()) {
                        return true;
                    }
                }
                return false;
            }
        };
    }
}

3.4 FactoryBean开发

负责接口代理类的创建和数据封装传递

public class HttpReqFactoryBean implements FactoryBean<Object> {

    private Class<?> type ; //接口类型

    private String url;

    private int timeout;

    private Class callback;

    @Override
    public Class<?> getObjectType() {
        return this.type;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},new HttpReqHolder(url,timeout,callback).newInstance());
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public Class getCallback() {
        return callback;
    }

    public void setCallback(Class callback) {
        this.callback = callback;
    }
}

3.5 处理器开发和数据封装

public class HttpReqHolder {

    private String url;

    private int timeout;

    private Class callback;

    public HttpReqHolder(String url, int timeout, Class callback) {
        this.url = url;
        this.timeout = timeout;
        this.callback = callback;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public Class getCallback() {
        return callback;
    }

    public void setCallback(Class callback) {
        this.callback = callback;
    }

    @Override
    public String toString() {
        return "HttpReqHolder{" +
                "url='" + url + '\'' +
                ", timeout=" + timeout +
                ", callback=" + callback +
                '}';
    }

    class MyInvocationHandler implements InvocationHandler{

        private HttpReqHolder holder;

        public MyInvocationHandler(HttpReqHolder holder) {
            this.holder = holder;
        }
        
        //代理方法,可以通过多传递RestTemplate来代理http请求
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(holder.toString());
            Class<?> returnType = method.getReturnType();
            RestfullReq annotation = method.getAnnotation(RestfullReq.class);
            String url = annotation.url();
            ReqMethod reqMethod = annotation.reqMethod();

            return null;
        }
    }

    public InvocationHandler newInstance() {
        return new MyInvocationHandler(this);
    }
}

3.6 注解的使用

启动类上开启自定义注解使用

@EnableHttpRequest
@SpringBootApplication
@EnableScheduling
public class MemberApplication implements ExitCodeGenerator {

	public static void main(String[] args) {
		ConfigurableApplicationContext run = SpringApplication.run(MemberApplication.class, args);
		GbaseInterface bean = run.getBean(GbaseInterface.class);
		bean.findByName();
//		System.out.println(-1<<8);
	}

在需要代理的接口上使用自定义注解

@ProxyHttp(url = "http://127.0.0.1:3360/test")
public interface GbaseInterface {

    @RestfullReq(url = "/getByName")
    void findByName();

    void  selectAll();

通过自定义注解@ProxyHttp生成接口代理类,代理http发送请求

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值