基于SpringBoot的代码跟踪框架demo

本篇博客将介绍如何写一个@EnableXXXX注解 实现方法简单的方法跟踪

需要准备的基础知识

  • 如何编写一个注解接口,以及注解参数设置
  • 反射类的原理以及常用api的使用方法
  • springboot bean初始化以及创建的过程~~(这个的内容有点多,可以大概了解一下)~~
  • cglib动态代理的使用方法

以上内容本篇文章不做过多的解释,如遇不懂之处请自行百度,google。

设计思路

顺序图

开始设计

  • 首先设计好要使用到的注解接口

	/**
 	* 用于指定需要方法跟踪的类
 	* @Author TomShiDi
 	*/
	@Documented
	Inherited
	@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER})
	@Retention(RetentionPolicy.RUNTIME)
	public @interface MethodLog {

    	String value() default "default value";
	
    	String preExeMessage() default "执行前的信息";

    	String afterExeMessage() default "执行后的信息";

	}
    

	/**
	 * 动态注册bean,起作用的是@Import这个注解
	 * @Author TomShiDi
	 */
	@Documented
	@Inherited
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	@Import({MethodLogResourceRegistry.class,DefaultProxyProcessor.class})
	public @interface EnableMethodLog {
	
	    /**
	     * 待扫描的包名
	     * @return
	     */
	    String scannedPkgName();
	
	}

自定义的注解就以上两个,接下来看一下动态注册bean是怎么实现的

/**
 * 动态注册实现类
 * @Author TomShiDi
 */
public class MethodLogResourceRegistry implements ImportBeanDefinitionRegistrar{

    private final static String ANNOTATION_ATTRIBUTE_NAME_PKG = "scannedPkgName";


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取EnableMethodLog注解的参数map
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableMethodLog.class.getName());
        //获取scannedPkgName这个参数的值
        String pkgName = (String) attributes.get(ANNOTATION_ATTRIBUTE_NAME_PKG);
        //扫描功能实现类
        AnnotationScannedMeta annotationScannedMeta = new AnnotationScannedFullAchieve();
        //过滤出带有@MethodLog注解的类
        AnnotationParserMeta annotationParserMeta = new ProxyMethodLogAnnotationParser(annotationScannedMeta.scannedCandidates(pkgName), MethodLog.class);
        Set<Class<?>> pointedClass = annotationParserMeta.getPointedClasses();

        //以下为动态注册的核心
        if (pointedClass != null && pointedClass.size() > 0) {
            pointedClass.forEach(e-> {
                String temp = BeanNameUtil.parseToBeanName(e.getName());
                //首先判断是否已经注册过,这里可有可无,因为设计这个判断的最初目的是为了兼容springboot的容器注解的,但是发现行不通,原因在后面讲
                if (!registry.containsBeanDefinition(temp)) {
                    //注册
                    registry.registerBeanDefinition(e.getName(), BeanDefinitionBuilder.rootBeanDefinition(e).getBeanDefinition());
                }
            });
        }
    }

}

具体的扫描以及过滤实现类代码我就不贴上来了,因为代码中有很多的冗余操作。实现起来也挺简单的,没必要班门弄斧。

运行起来的情况是这样的

动态注册

OriginPeople类是这样的

@MethodLog
public class OriginalPeople {

    private List<String> sayingList;

    public OriginalPeople() {
        this.sayingList = new ArrayList<>();
    }

    public void saySomething(String saying) {
        sayingList.add(saying);
        System.out.println("saying :" + saying + " sayingList size :" + sayingList.size());
    }
}

接着我们来看@Import引入的另外一个类DefaultProxyProcessor

/**
 * bean实例化完成以后将要执行的处理器函数
 * @Author TomShiDi
 * @Since 2019/6/11
 * @Version 1.0
 */
public class DefaultProxyProcessor implements BeanPostProcessor,ApplicationContextAware {

    private ApplicationContext context;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz;
        Object proxyBean;
        //如果bean包含MethodLog注解,对该类进行动态代理
        if ((clazz = bean.getClass()).isAnnotationPresent(MethodLog.class)) {
            ProxyMethodLogAnnotationParser proxyMethodLogAnnotationParser = new ProxyMethodLogAnnotationParser(clazz, MethodLog.class);
            //获取cglib动态代理后的实例对象
            proxyBean = proxyMethodLogAnnotationParser.getProxyInstance(bean.getClass());
//            if (context.containsBean(beanName))
//            BeanUtils.copyProperties(bean,proxyBean);
        }else {
            proxyBean = bean;
        }
        return proxyBean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
}

代理过程截图

cglib代理运行图

最后看一下怎么调用

/**
 * @Author TomShiDi
 */
@SpringBootApplication
@EnableMethodLog(scannedPkgName = "barrage.demo.methodlog")//加入这个注解
public class DemoApplication {

	//不要在static变量上写@Autowired,无法注入
    private static OriginalPeople originalPeople;


    public static void main(String[] args) throws Exception{
        SpringApplication.run(DemoApplication.class, args);
        originalPeople.saySomething("Hello World");
    }


    @Autowired
    public void setOriginalPeople(OriginalPeople originalPeople) {
        DemoApplication.originalPeople = originalPeople;
    }
}

执行截图

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tomshidi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值