1. 程式人生 > 其它 >SpringBoot 基於註解實現介面的代理Bean注入

SpringBoot 基於註解實現介面的代理Bean注入

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();
}
}

}