期望目标
提供一个可以扫描指定包的注解该包下,全部为接口类型在spring boot环境中,可以正常注入扫描包内的全部接口扫描包内的接口,使用代理模式实现,且,方法执行时,执行自定义代码段
思路解析
自定义一个注解该注解需要具备一个数组参数,用于存储扫描包的路径已知包内均为接口类型,也就是说,没有实现类,ioc注入必然报错,需要动态创建代理类代理模式两种方案,jdk需要预先实现相关接口,不太方便,cglib可能会更方便一些,所以使用cglib去实现相关功能该代理实现需要被ioc管理
代码实现
pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.5.4
com.example
demo
0.0.1-SNAPSHOT
demo
demo
1.8
commons-io
commons-io
2.2
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
org.springframework.boot
spring-boot-maven-plugin
设计CGLIB代理拦截器,进行方法拦截
package com.example.demo.bean;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 获取被代理目标的真实 class
Class> targetClass = obj.getClass();
boolean aopProxy = AopUtils.isAopProxy(obj);
if (aopProxy) {
targetClass = AopUtils.getTargetClass(obj);
}
String name = targetClass.getName();
// 参考cglib代理实现命名规范
String[] split = name.split("\$\$");
System.out.println("class name =====>" + split[0]);
System.out.println("method name =====>" + method.getName());
return null;
}
}
设计一个代理实现类 MyProxyBean
package com.example.demo.bean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cglib.proxy.Enhancer;
public class MyProxyBean implements FactoryBean {
private Class myInterfaceClass;
public MyProxyBean(Class myInterfaceClass) {
this.myInterfaceClass = myInterfaceClass;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
@Override
public T getObject() throws Exception {
//用于创建代理对象的增强器,可以对目标对象进行扩展
Enhancer enhancer = new Enhancer();
//将目标对象设置为父类
enhancer.setSuperclass(myInterfaceClass);
//设置目标拦截器
enhancer.setCallback(new MyProxyInterceptor());
// 创建代理对象
return (T)enhancer.create();
}
@Override
public Class getObjectType() {
return myInterfaceClass;
}
}
自定义路径扫描器 MyProxyScan
package com.example.demo.bean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.classreading.metadataReader;
import java.io.IOException;
import java.util.Set;
public class MyProxyScan extends ClassPathBeanDefinitionScanner {
public MyProxyScan(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected Set doScan(String... basePackages) {
return super.doScan(basePackages);
}
@Override
protected boolean isCandidateComponent(metadataReader metadataReader) throws IOException {
return metadataReader.getClassmetadata().isInterface();
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getmetadata().isInterface();
}
}
自定义注解解析器 MyProxyBeanimportSelector
package com.example.demo.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.importSelector;
import org.springframework.core.type.Annotationmetadata;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import java.util.Set;
@Configuration
public class MyProxyBeanimportSelector implements importSelector, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public String[] selectimports(Annotationmetadata importingClassmetadata) {
// 转换为 DefaultListableBeanFactory 便于操作
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
// 获取自定义注解的具体数据信息
Map annotationAttributes = importingClassmetadata.getAnnotationAttributes(MyProxy.class.getName());
if (!CollectionUtils.isEmpty(annotationAttributes)) {
// 获取配置的扫描包路径
String[] scanPackages = (String[]) annotationAttributes.get("scanPackages");
// 自定义扫描器,获取 BeanDefinitionHolder
MyProxyScan myProxyScan = new MyProxyScan(defaultListableBeanFactory);
Set beanDefinitionHolders = myProxyScan.doScan(scanPackages);
// 循环处理每个被扫描到的bean对象,创建代理实现类
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 转换为抽象实现,便于操作具体对象
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanDefinitionHolder.getBeanDefinition();
try {
// 获取当前被扫描到到接口到具体 class name
String beanClassName = beanDefinition.getBeanClassName();
// 装载 class
Class> targetInterface = Class.forName(beanClassName);
// 为该自定义的代理实现类,提供构造方法所需要的原始接口类型参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(targetInterface);
// 修改被扫描接口到具体实现类型,替换为自定义到代理实现类
beanDefinition.setBeanClassName(MyProxyBean.class.getName());
String beanName = beanDefinitionHolder.getBeanName();
System.out.println("已修改===>" + beanName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
// 故意返回空
return new String[0];
}
}
自定义注解 MyProxy
package com.example.demo.bean;
import org.springframework.context.annotation.import;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
@import(MyProxyBeanimportSelector.class)
public @interface MyProxy {
String[] value();
}
在启动器添加自定义注解
package com.example.demo;
import com.example.demo.bean.MyProxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MyProxy(scanPackages = "com.example.demo.post")
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
System.out.println("-------------------------------start done-----------------------------------------");
}
}