实现动态代理接口并注入到IOC容器的第二种方式:ImportBeanDefinitionRegistrar+ClassPathBeanDefinitionScanner
准备工作:
- 编写扫描包注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 使用import的方式导入
@Import(AutoMapperScanImportBeanDefinitionRegistrar.class)
public @interface MapperScan {
@AliasFor("value")
String[] basePackage() default {};
@AliasFor("basePackage")
String[] value() default {};
}
- 编写注解,用来识别哪些接口需要被动态代理(类似于
@Repository
)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName NeedProxy
* @Description TODO
* @Author Silwings
* @Date 2021/3/7 15:57
* @Version V1.0
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedProxy {
String value() default "";
}
- 编写公共接口(类似于
@Mapper
)
/**
* @ClassName Repository
* @Description 泛型用来声明实体类类型(参考MyBatis)
* @Author Silwings
* @Date 2021/3/7 15:58
* @Version V1.0
**/
public interface Repository<T> {
// 示例方法.
String print();
}
- 编写公共接口的默认实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* @ClassName DefaultRepository
* @Description TODO
* @Author Silwings
* @Date 2021/3/7 16:00
* @Version V1.0
**/
public class DefaultRepository<T> implements Repository<T> , InvocationHandler {
// 这里声明一个Class,用来接收接口声明的泛型实际类型的class,T是声明的实体类类型
private Class<T> clazz;
public DefaultRepository(Class<T> interfaceType) {
// 获取当前类上的泛型类型
ParameterizedType parameterizedType = (ParameterizedType) interfaceType.getGenericInterfaces()[0];
// 获取泛型对应的真实类型(泛型真实类型在很多场合需要使用)
Type[] actualType = parameterizedType.getActualTypeArguments();
// 取数组的第一个,肯定是T的类型,即实体类类型(如果有多个,递增角标即可)
// 需要注意,继承BaseRepository的接口不能定义泛型,否则会出现类型转换异常
this.clazz = (Class<T>) actualType[0];
}
@Override
public String print() {
// 示例方法的默认实现
System.out.println(clazz);
return clazz.getName();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object 方法,走原生方法,比如hashCode()
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this,args);
}
// 其它走本地代理
return method.invoke(this, args);
}
}
- 默认实现完成后,需要使用
FactoryBean
来构建它.
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.Proxy;
/**
* @ClassName RepositoryFactory
* @Description FactoryBean是一种特殊的Bean,其返回的对象不是指定类的一个实例,其返回的是该工厂Bean的getObject方法所返回的对象
* @Author Silwings
* @Date 2021/3/7 16:01
* @Version V1.0
**/
public class RepositoryFactory<T> implements FactoryBean<T> {
/**
* 构建DefaultRepository需要使用的参数
*/
private Class<T> interfaceType;
public RepositoryFactory(Class<T> interfaceType) {
this.interfaceType = interfaceType;
}
@Override
public T getObject() throws Exception {
// 因为DefaultRepository需要Class<T>作为参数,所以该类包含一个Claa<T>的成员,通过构造函数初始化
return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[]{interfaceType},
new DefaultRepository<>(interfaceType));
}
@Override
public Class<?> getObjectType() {
// 该方法返回的getObject()方法返回对象的类型,这里是基于interfaceType生成的代理对象,所以类型就是interfaceType
return interfaceType;
}
}
核心工作
该方法的核心是需要继承和实现ClassPathBeanDefinitionScanner
和ImportBeanDefinitionRegistrar
.通过自定义类扫描器完成类的扫描工作
- 继承
ClassPathBeanDefinitionScanner
import com.silwings.mapperdemo.anno.Repository;
import com.silwings.mapperdemo.repository.RepositoryFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import java.util.Set;
/**
* @ClassName AutoMapperScanClassPathBeanDefinitionScanner
* @Description TODO
* @Author Silwings
* @Date 2021/3/13 10:00
* @Version V1.0
**/
public class AutoMapperScanClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public AutoMapperScanClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
/**
* description: 负责对接口代理进行定义
* version: 1.0
* date: 2021/3/13 11:04
* author: Silwings
*
* @param beanDefinitionHolderSet
* @return void
*/
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 添加过滤器,只扫描添加了NeedProxy注解的类
addIncludeFilter(new AnnotationTypeFilter(NeedProxy.class));
Set<BeanDefinitionHolder> beanDefinitionHolderSet = super.doScan(basePackages);
// 对扫描到的数据进行代理处理
processBeanDefinitions(beanDefinitionHolderSet);
return beanDefinitionHolderSet;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitionHolderSet) {
beanDefinitionHolderSet.forEach(e -> {
// 设置工厂等操作需要基于GenericBeanDefinition,BeanDefinitionHolder是其子类
GenericBeanDefinition definition = (GenericBeanDefinition) e.getBeanDefinition();
// 获取接口的全路径名称
String beanClassName = definition.getBeanClassName();
// 设置构造函数参数
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
// 设置工厂
definition.setBeanClass(RepositoryFactory.class);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
});
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
}
- 实现
ImportBeanDefinitionRegistrar
,这样可以对自定义的类路径扫描器创建对象进行扫描
import com.silwings.mapperdemo.anno.MapperScan;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
/**
* @ClassName AutoMapperScanImportBeanDefinitionRegistrar
* @Description TODO
* @Author Silwings
* @Date 2021/3/13 9:59
* @Version V1.0
**/
public class AutoMapperScanImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取MapperScan注解属性信息
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// 获取注解的属性值,拿到定义的扫描路径
String[] basePackages = annotationAttributes.getStringArray("basePackage");
// 使用自定义扫描器扫描
AutoMapperScanClassPathBeanDefinitionScanner scanner = new AutoMapperScanClassPathBeanDefinitionScanner(registry, false);
scanner.doScan(basePackages);
}
}
使用:
- 在启动类上添加
MapperScan
注解,并指定需要扫描哪个路径
@SpringBootApplication
@MapperScan({"com.silwings.mapperdemo.mapper"})
public class MapperApplication {
public static void main(String[] args) {
SpringApplication.run(MapperApplication.class, args);
}
}
- 继承
Repository
@NeedProxy
public interface Test03 extends Repository<User> {
}
- 编写controller
@RestController
@RequestMapping("/my")
public class TestController {
private Test03 test03;
@Autowired
public TestController(Test03 test03) {
this.test03 = test03;
}
@GetMapping("/test03")
public String test03() {
Objects.requireNonNull(test03, "你的代码怎么又报错啦!");
System.out.println("测试 getClass() = " + test03.getClass());
System.out.println("测试 hashCode() = " + test03.hashCode());
return test03.print();
}
}
- 请求
localhost:8080/my/test03
.
- 控制台打印
测试 getClass() = class com.sun.proxy.$Proxy49
测试 hashCode() = 1018602505
class com.silwings.mapperdemo.bean.User
- 请求响应结果
com.silwings.mapperdemo.bean.User
说明已经成功对接口进行代理并注入到了Spring容器,同时像hashCode()这种Object的方法也可以正常执行.