以上内容本篇文章不做过多的解释,如遇不懂之处请自行百度,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 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> 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 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;
}
}
代理过程截图
最后看一下怎么调用
/**
* @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;
}
}
执行截图