SpringBoot 基于注解實作接口的代理Bean注入
在springboot加載時需自己手動将接口的代理bean注入到spring容器中,這樣在service層注入該接口類型即可,
1.在SpringBoot啟動類上添加EnableProxyBeanScan注解

EnableProxyBeanScan為自定義注解,通過Import注解掃描被ProxyBean注解的類或者被ProxyBean修飾的注解注解的類("注解繼承")
ProxyBeanDefinitionRegistrar實作ImportBeanDefinitionRegistrar 通過ProxyInterfaceBeanBeanDefinitionScanner 來進行bean的加載
ProxyFactoryBean為bean的工廠類,提供代理bean
ProxyHandler為代理業務邏輯接口,提供三個參數: Class(被代理的類),Method(被代理的方法),Object[] 入參參數
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(EnableProxyBeanScan.ProxyBeanDefinitionRegistrar.class)
public @interface EnableProxyBeanScan {
String[] basePackages() default {};
class ProxyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ProxyInterfaceBeanBeanDefinitionScanner scanner = new ProxyInterfaceBeanBeanDefinitionScanner(registry);
scanner.scan(getBasePackages(importingClassMetadata));
}
private String[] getBasePackages(AnnotationMetadata importingClassMetadata){
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableProxyBeanScan.class.getCanonicalName());
Set<String> basePackages = new HashSet();
String[] basePackagesArr = (String[])((String[])attributes.get("basePackages"));
for(String item: basePackagesArr){
if(StringUtils.hasText(item))
basePackages.add(item);
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages.toArray(new String[basePackages.size()]);
}
}
}
public class ProxyInterfaceBeanBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public ProxyInterfaceBeanBeanDefinitionScanner(BeanDefinitionRegistry registry) {
//registry是Spring的Bean注冊中心
// false表示不使用ClassPathBeanDefinitionScanner預設的TypeFilter
// 預設的TypeFilter隻會掃描帶有@Service,@Controller,@Repository,@Component注解的類
super(registry,false);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
addIncludeFilter(new AnnotationTypeFilter(ProxyBean.class));
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
if (beanDefinitionHolders.isEmpty()){
System.err.println("No Interface Found!");
}else{
//建立代理對象
createBeanDefinition(beanDefinitionHolders);
}
return beanDefinitionHolders;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isInterface() || metadata.isAbstract();
}
/**
* 為掃描到的接口建立代理對象
* @param beanDefinitionHolders
*/
private void createBeanDefinition(Set<BeanDefinitionHolder> beanDefinitionHolders) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
GenericBeanDefinition beanDefinition = ((GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition());
//将bean的真實類型改變為FactoryBean
beanDefinition.getConstructorArgumentValues().
addGenericArgumentValue(beanDefinition.getBeanClassName());
beanDefinition.setBeanClass(ProxyFactoryBean.class);
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ProxyBean {
Class<? extends ProxyHandler> value();
}
public interface ProxyHandler{
Object execute(Class<?> proxyType, Method proxyMethod, Object[] args);
}
public class ProxyFactoryBean<T> implements FactoryBean {
private static final Map<Class<? extends ProxyHandler>,ProxyHandler> ProxyHandlers = new ConcurrentHashMap<>();
private Class<T> interfaceClass;
private Class<? extends ProxyHandler> proxyHandlerType;
public ProxyFactoryBean(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
this.proxyHandlerType = AnnotationUtils.findAnnotation(interfaceClass, ProxyBean.class).value();
if(!ProxyFactoryBean.ProxyHandlers.containsKey(proxyHandlerType)) {
ProxyHandler proxyHandler = ClassUtils.newInstance(proxyHandlerType);
SpringBean.inject(proxyHandler);
ProxyFactoryBean.ProxyHandlers.put(proxyHandlerType, proxyHandler);
}
}
@Override
public T getObject() throws Exception {
final ProxyHandler proxyHandler = ProxyFactoryBean.ProxyHandlers.get(proxyHandlerType);
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class[]{interfaceClass},
(proxy,method,args) -> proxyHandler.execute(interfaceClass,method,args)
);
}
@Override
public Class<T> getObjectType() {
return interfaceClass;
}
}
簡單的例子:
類似spring-feign的接口發送Http請求
1.先定義一個注解HttpClient,和HttpClientProxyHandler
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ProxyBean(HttpClient.HttpClientProxyHandler.class)
public @interface HttpClient {
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Request{
String url();
RequestMethod method() default RequestMethod.POST;
}
/**簡單定義下進行測試,實際實作肯定要比這個複雜*/
class HttpClientProxyHandler implements ProxyHandler {
/**這個類雖然沒有被Spring管理,不過通過這個注解可以實作SpringBean的注入和使用,
* 見ProxyFactoryBean構造方法的代碼
* SpringBean.inject(proxyHandler);
*/
@Autowired
private RestTemplate template;
@Override
public Object execute(Class<?> proxyType, Method proxyMethod, Object[] args) {
return template.postForObject(
proxyMethod.getAnnotation(Request.class).url()
,args[0]
,proxyMethod.getReturnType()
);
}
}
}
2.被代理的接口
@HttpClient
public interface LoginService {
@HttpClient.Request(url="ddd")
String getUserAge(ExamineReqDto username);
}
3.測試,
測試這裡沒有細緻的測,RestTemplate這裡是成功拿到了,不影響後續的使用
最後,附Bean注入的代碼:
@Component
public class SpringBean implements ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(SpringBean.class);
private static ApplicationContext applicationContext;
private SpringBean(){}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringBean.applicationContext = applicationContext;
}
public static <T> T getSpringBean(Class<T> clazz){
return SpringBean.applicationContext.getBean(clazz);
}
@SuppressWarnings("unchecked")
public static <T> T getSpringBean(String beanName){
return (T) SpringBean.applicationContext.getBean(beanName);
}
public static void inject(Object object){
if(object == null)
return;
Class clazz = object.getClass();
while (clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if (annotation != null) {
Reflector.setFieldValue(object,field,SpringBean.getSpringBean(field.getType()));
}
Resource resource = field.getAnnotation(Resource.class);
if (resource != null) {
Reflector.setFieldValue(object,field,SpringBean.getSpringBean(field.getName()));
}
}
clazz = clazz.getSuperclass();
}
}
}