首先,说明一下功能需求,平时定义一个接口,就要使用implements关键字来实现接口。那么,当不使用此关键字的时候,是否也能使相关接口也能够绑定实现类呢?
答案是肯定的。
此篇文章的主要功能有两个:
1)从实现原理上,更深层次的理解mybatis的映射逻辑;
2)此功能实战中可以通过配置的方式,在不同环境或者客户中执行不同的业务逻辑;
1.创建接口和实现类,但不使用implements
接口:
public interface ProductService {
void getProductName(String name);
}
未实现implements关键字的实现类:
@ImplService(parentUrl = "com.example.springdragoncommon.hbl.yms.spring.mapper.service.ProductService")
public class ProductServiceImpl {
public ProductServiceImpl(){
System.out.println("我是构造函数!");
}
public void getProductName(String name){
System.out.println("我是一个被代理的实现方法!");
}
}
可以看到此时没有使用implements关键字,但是使用了一个@ImplService自定义注解,这里的注解就有点类似于mybaties中的<mapper namespace="">
2.创建自定义注解
这里需要两个自定义注解:
1)@ServiceScan注解
用于定义需要扫描的路径,类似于mybatis中的MapperScan功能
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ScanServiceImportBeanDefinitionRegistrar.class)
public @interface ServiceScan {
/**
* 需要扫描的实现类的路径
* @return
*/
String[] packageScan() default {};
}
2)@ImplService注解
此注解就是指定此类实现了哪一个接口的功能。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ImplService {
String parentUrl() default "";
}
3.ScanServiceImportBeanDefinitionRegistrar类
此类是ServiceScan注解中,使用@Import注解引入的类,它实现ImportBeanDefinitionRegistrar接口,
public class ScanServiceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
List<String> packages = findPackages(importingClassMetadata);
ClassPathScanner classPathScanner = new ClassPathScanner(registry);
classPathScanner.addIncludeFilterCustom();
classPathScanner.doScan(StringUtils.toStringArray(packages));
}
/**
* 获取扫描注解解析的类
* @param importingClassMetadata
* @return
*/
private List<String> findPackages(AnnotationMetadata importingClassMetadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ServiceScan.class.getName()));
List<String> packages = new ArrayList<>();
for (String className :annotationAttributes.getStringArray("packageScan")){
if (StringUtils.hasText(className)) {
packages.add(className);
}
}
return packages;
}
}
此处主要的类是ClassPathScanner类,它本身实现了ClassPathBeanDefinitionScanner类,重写了doScan方法和addIncludeFilter方法,
public class ClassPathScanner extends ClassPathBeanDefinitionScanner {
private BeanDefinitionRegistry registry;
private ScanClassBeanFactory scanClassBeanFactory = new ScanClassBeanFactory();
public ClassPathScanner(BeanDefinitionRegistry registry) {
super(registry);
this.registry=registry;
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
if (beanDefinitionHolders == null || beanDefinitionHolders.isEmpty()){
logger.error("not find target class");
}else {
this.postProcessBeanDefinition(beanDefinitionHolders);
}
return beanDefinitionHolders;
}
protected void postProcessBeanDefinition(Set<BeanDefinitionHolder> beanDefinitionHolders){
if (beanDefinitionHolders ==null || beanDefinitionHolders.isEmpty()){
return;
}
//此处为了防止多实现,防止注入异常,默认第一个加载
Map<String,String> removeMap = new ConcurrentHashMap<>();
beanDefinitionHolders.stream().forEach(p->{
ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) p.getBeanDefinition();
String parentUrl = beanDefinition.getMetadata().getAnnotations().get(ImplService.class).getString("parentUrl");
if (!StringUtils.hasText(removeMap.get(parentUrl))){
String targetClassName = beanDefinition.getMetadata().getClassName();
try {
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,Class.forName(parentUrl));
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1,Class.forName(targetClassName));
beanDefinition.setBeanClass(this.scanClassBeanFactory.getClass());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
removeMap.put(parentUrl,targetClassName);
}
});
}
public void addIncludeFilterCustom() {
//添加扫描拦截器判断
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
}
1)doScan方法
调用了父类的doScan方法,即获取当前basePackages下的所有实现类的BeanDefinitionHolder,
2)postProcessBeanDefinition方法
此方法为关键方法:
它首先获取了实现类的bean定义,即上面ProductServiceImpl类的bean定义;
然后获取到了要实现的接口,然后通过bean定义提供的操作,将ProductServiceImpl的bean定义转换成了ScanClassBeanFactory的bean定义,即一个实现类对应一个ScanClassBeanFactory的bean定义;
在此过程中,就可以进行不同环境或客户提取不同的实现类,此处没有实现,可以直接配置一个环境变量,类似于key-value这种参数,不同环境下取什么实现类,然后在此处判断处理即可;
3)addIncludeFilterCustom方法
此处添加的是类生成定义时候使用的过滤器,不重写的话可能存在问题,生成不了自己需要的bean定义
4.ScanClassBeanFactory类
public class ScanClassBeanFactory<T> implements FactoryBean {
private Class<T> targetClass;
private Class<T> targetImplClassName;
public ScanClassBeanFactory(){
}
public ScanClassBeanFactory(Class<T> targetClass, Class<T> targetImplClassName) {
this.targetClass = targetClass;
this.targetImplClassName = targetImplClassName;
}
@Override
public Object getObject() throws Exception {
Object object = targetImplClassName.newInstance();
BeanScanInvocationHandler beanScanInvocationHandler = new BeanScanInvocationHandler(object,targetClass);
return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{targetClass}, beanScanInvocationHandler);
}
@Override
public Class<?> getObjectType() {
return this.targetClass;
}
}
它实现了FactoryBean接口,所以会调用一次getObject方法,此方法使用了代理方式,即给接口代理实际的实现类;
5.BeanScanInvocationHandler类
public class BeanScanInvocationHandler implements InvocationHandler {
private Object target;
private Class<?> interfaces;
private Map<Method,Method> methodMap;
public BeanScanInvocationHandler(Object target,Class<?> interfaces){
this.target = target;
this.interfaces = interfaces;
this.methodMap = getMethods(target.getClass(),interfaces);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method targetMethod = methodMap.get(method);
return targetMethod.invoke(target,args);
}
private Map<Method, Method> getMethods(Class<?> delegate, Class<?>... interfaces){
Map<Method, Method> map;
List<Method> methods = new ArrayList<>();
for (Class<?> sourceClass : interfaces) {
methods.addAll(getMethods(sourceClass));
}
map = new HashMap<>(methods.size(), 1.0f);
for (Method method : methods) {
try {
map.put(method, delegate.getMethod(method.getName(), method.getParameterTypes()));
} catch (NoSuchMethodException ignore) {
throw new RuntimeException(ignore);
}
}
return map;
}
private Collection<? extends Method> getMethods(Class<?> sourceClass) {
Set<Method> result = new HashSet<>();
Class<?> searchType = sourceClass;
while (searchType != null && searchType != Object.class) {
result.addAll(filterPublicMethods(Arrays.asList(sourceClass.getDeclaredMethods())));
if (sourceClass.isInterface()) {
Class<?>[] interfaces = sourceClass.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
result.addAll(getMethods(interfaceClass));
}
searchType = null;
} else {
searchType = searchType.getSuperclass();
}
}
return result;
}
private Collection<? extends Method> filterPublicMethods(List<Method> methods) {
List<Method> result = new ArrayList<>(methods.size());
for (Method method : methods) {
if (Modifier.isPublic(method.getModifiers())) {
result.add(method);
}
}
return result;
}
}
此方法主要先通过接口获取其所有的类信息,然后在通过代理的实现调用实现类的对应方法;
到此,功能代码结束,运行一下看一下效果
此处使用了postman调用,直接注入接口调用即可;代码很简单就不在写出来了。