通过注解动态指定实现类,实现spring bean的动态代理
目录
当我们的一个接口需要被多次实现时,如果不用动态代理或者实现类,那么我们的代码就会十分的冗余,到处都是impl的实现类注入,很不友好。为此,我们可以通过动态代理类,将生成的对象注入到Spring容器中,业务层只要继承我们的上游接口,再通过注解指定名称的方式达到实时动态获取实现类。
实现动态代理需要实现的类如下
- BeanDefinitionRegistryPostProcessor
实现自定义的注册bean定义的逻辑,官方解释
/**
* Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
* the registration of further bean definitions <i>before</i> regular
* BeanFactoryPostProcessor detection kicks in. In particular,
* BeanDefinitionRegistryPostProcessor may register further bean definitions
* which in turn define BeanFactoryPostProcessor instances.
**/
- ApplicationContextAware
实现这个接口可以方便的获取所有的bean
- FactoryBean
由{@link BeanFactory}中使用的对象实现本身就是单个对象的工厂。如果bean实现了这个接口,它被用作对象公开的工厂,而不是直接作为将自己公开的Bean实例。
- InvocationHandler
代理实现类的处理器
demo联系
需要应用的依赖
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
先定义好自己的注解,为后续方法上指定做准备
工作类型注解,工作实现的方法上使用该注解
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WorkHandoverType {
String type();
}
工作默认实现类注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface HandlerWorkAutoImpl {
String name();
}
自定义factorybean----HandlerInterfaceFactoryBean
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
@Slf4j
@Data
public class HandlerInterfaceFactoryBean<T> implements FactoryBean<T> {
private Class<T> interfaceClass;
private String typeName;
private ApplicationContext context;
@Override
public T getObject() {
return (T) DynamicProxyBeanFactory.newMapperProxy(typeName, context, interfaceClass);
}
@Override
public Class<?> getObjectType() {
return interfaceClass;
}
@Override
public boolean isSingleton() {
return true;
}
}
实现InvocationHandler
package io.hcbm.boot.wm.dynamic;
import io.hcbm.boot.wm.annotation.WorkHandoverType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class DynamicProxyBeanFactory implements InvocationHandler {
private String className;
private ApplicationContext applicationContext;
private Map<String, Object> clientMap = new HashMap<>();
public DynamicProxyBeanFactory(String className, ApplicationContext applicationContext) {
this.className = className;
this.applicationContext = applicationContext;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (clientMap.size() == 0) {
initClientMap();
}
String workType = (String) args[0];
return clientMap.get(workType);
}
private void initClientMap() throws ClassNotFoundException {
Map<String,?> classMap = applicationContext.getBeansOfType(Class.forName(className));
log.info("DynamicProxyBeanFactory className: [{}] , impl class: [{}]",className,classMap);
for (Map.Entry<String,?> entry : classMap.entrySet()) {
//这里注意下,获取注解别名,显式别名、隐式别名、传递的隐式别名,用这个方法,可能service层直接通过class获取不到。
WorkHandoverType workHandoverType = AnnotationUtils.findAnnotation(entry.getValue().getClass(),
WorkHandoverType.class);
if (workHandoverType == null) {
continue;
}
clientMap.put(workHandoverType.type(), entry.getValue());
}
log.info("DynamicProxyBeanFactory clientMap: [{}]",clientMap);
}
public static <T> T newMapperProxy(String classStr,ApplicationContext applicationContext,Class<T> mapperInterface) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces = new Class[] {mapperInterface};
DynamicProxyBeanFactory proxy = new DynamicProxyBeanFactory(classStr, applicationContext);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}
自定义实现HandlerBeanDefinitionRegistry
package io.hcbm.boot.wm.dynamic;
import io.hcbm.boot.wm.annotation.HandlerWorkAutoImpl;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Set;
@Slf4j
@Component
public class HandlerBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
private static final String INTERFACE_CLASS = "interfaceClass";
private static final String TYPE_NAME = "typeName";
private static final String CONTEXT = "context";
private ApplicationContext applicationContext;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
Set<Class<?>> classes = getAutoWorkClasses();
for (Class<?> clazz : classes) {
Type[] types = clazz.getGenericInterfaces();
ParameterizedType type = (ParameterizedType) types[0];
String typeName = type.getActualTypeArguments()[0].getTypeName();
HandlerWorkAutoImpl handlerRouterAutoImpl = clazz.getAnnotation(HandlerWorkAutoImpl.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getPropertyValues().add(INTERFACE_CLASS, clazz);
definition.getPropertyValues().add(TYPE_NAME, typeName);
definition.getPropertyValues().add(CONTEXT, applicationContext);
definition.setBeanClass(HandlerInterfaceFactoryBean.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinitionRegistry.registerBeanDefinition(handlerRouterAutoImpl.name(), definition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
private Set<Class<?>> getAutoWorkClasses() {
Reflections reflections = new Reflections(
"io.hcbm.*",//这里包路径改为自己的
new TypeAnnotationsScanner(),
new SubTypesScanner());
return reflections.getTypesAnnotatedWith(HandlerWorkAutoImpl.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
以上我们的动态代理需要重新实现的方法完成*
开始业务层的demo coding
抽象一个HandlerRouter范型接口
public interface HandlerRouter<T> {
/**
* 工作交接处理方法
* @param workType 工作交接类型
* @param args 参数
* @return T
*/
T workHandler(String workType, Object... args);
}
创建我们需要的工作接口定义
public interface WorkHandler {
/**
* 工作交接执行方法
* @param workType 工作交接类型
* @param obj obj
*/
void invoke(String workType, Object obj);
}
再抽象一层工作的接口,同时在接口上添加@HandlerWorkAutoImpl注解
import io.hcbm.boot.wm.annotation.HandlerWorkAutoImpl;
@HandlerWorkAutoImpl(name = "workHandlerRouter")
public interface WorkHandlerRouter extends HandlerRouter<WorkHandler> {
/**
*
* @param workType
* @return
*/
WorkHandler getWorkHandler(String workType);
}
创建工作的controller
import com.alibaba.fastjson.JSONObject;
import io.hcbm.boot.wm.service.WorkHandler;
import io.hcbm.boot.wm.service.WorkHandlerRouter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/v1/work")
public class WorkHandoverController {
@Autowired
private WorkHandlerRouter deviceHandlerRouter;
@GetMapping
@Permission(permissionPublic = true)
public ResponseEntity<Object> test() {
WorkHandler work01 = deviceHandlerRouter.workHandler("work01",null);
System.out.println("----WorkHandler---实现类-->" + work01);
WorkHandler work02 = deviceHandlerRouter.workHandler("work02", null);
System.out.println("----WorkHandler---实现类-->" + work02);
return ResponseEntity.ok("success");
}
}
创建两个工作的实现demo类
import io.hcbm.boot.wm.annotation.WorkHandoverType;
import io.hcbm.boot.wm.service.WorkHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@WorkHandoverType(type = "work01")
@Component
@Slf4j
public class Work01HandlerImpl implements WorkHandler {
@Override
public void invoke(String workType, Object obj) {
System.out.println("Work01HandlerImpl---------> " + workType);
}
}
import io.hcbm.boot.wm.annotation.WorkHandoverType;
import io.hcbm.boot.wm.service.WorkHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@WorkHandoverType(type = "work02")
@Component
@Slf4j
public class Work02HandlerImpl implements WorkHandler {
@Override
public void invoke(String workType, Object obj) {
System.out.println("Work02HandlerImpl---------> " + workType);
}
}
运行结果
- 可以发现,我们没有实现具体的impl,但通过传参数可以动态指定具体实现哪个service