代码之家(www.adminjie.com)精品网站源码,微信源码,游戏源码分享平台。《交流群:1037940574》
当前位置:网站首页 技术文章 网页制作 正文

如何利用Spring的@Import扩展点与spring进行无缝整合

时间:2020-08-08 [网页制作]作者:admin 浏览:130 次

利用Spring的@Import扩展与spring进行无缝整合前言BeanFactoryPostProcessor@Import实现POM文件定义数据层Resource(dao)层的扫描注解定义我的数据层Resource使用的注解ArteryResourceImportBeanDefinitionRegistrar实现自定义扫描类ClassPathArteryResourceScanner代理注册工厂ResourceRegistryResouce的代理工厂真正的代理类方法调用类AbstractBeanDefinitionFactory我们编写测试,来启动我们的spring容器类图

前言

spring有那些扩展呢?
spring的扩展非常多,比较常用的就是
BeanFactoryPostProcessor 我们可以插手spring bean工厂的初始化
BeanPostProcessor 我们可以插手spring bean实例化前后(比如SPRING AOP)
@Import
ImportAware。

BeanFactoryPostProcessor

spring的扩展点之一BeanFactoryPostProcessor,这个学名叫spring的Bean工厂后置处理器,
它可以插手spring bean工厂的实例化,我们可以启动spring的时候自己手动注册一个bean工厂后置处理器,它能做的事情太多,研究过spring源码的同学都知道,spring容器启动时候,会先暴露一个工厂出来,这个工厂就是DefaultListableBeanFactory,这里面放置了我们的BeanDeinition,我们都知道spring 单例bean容器放了很多单例的bean,而这些bean最后都是来自于DefaultListableBeanFactory中的bd容器;
BeanFactoryPostProcessor是spring提供给我们来扩展spring的,当然了它自己也在用,spring有自己内部的bean工厂后置处理器,处理的时候讲我们的和spring自己的一起处理。我们只需要把我们新建的类实现了BeanFactoryPostProcessor,并且加入@Component或者交给@Import就可以了。实现这个接口必须实现它的一个方法,它的这个方法就可以得到我们的beanDefinittionMap,也就是bdmap,这里面放置了我们系统所有的注册到spring容器里面的bd,最后spring循环这个bd,将其实例化成对象放入Bean容器。

今天我们的主题是使用sprinng的扩展点之一的@Import来实现公司的平台与spring整合,类似于Mybatis与spring整合一样

@Import

这个要说就要说很久,如果没有研究过spring底层源码的,可以去研究下,功能非常强大这边我大概介绍一下:
@import支持3中类型:
普通类(spring管理的类):就是讲一个普通的类通过@import导入,而不适用@Component,但是这样做毫无意义。
实现了ImportSelector:实现这个接口要求实现它的一个方法返回一个类名列表
Registrar:真正牛逼的注册类,实现了它,我们可以手动往里面添加自己的BeanDefiniton,自己实现扫描机制,自己实现很多很多自己的逻辑(mybatis整合spring就用的它)

实现

我的工程命名是:xxx-spring-platform-1.0.REALSE
xxx是公司的简称
工程结构:

如何利用Spring的@Import扩展点与spring进行无缝整合  2

其中context是核心,core是一些常用的核心类,aop写了一半,还没完成

POM文件

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>

就是用了srping的几个基础包

定义数据层Resource(dao)层的扫描注解

@Documented
@Retention(RUNTIME)
@Target(TYPE)
@Import(ArteryResourceImportBeanDefinitionRegistrar.class)
//这个是用了spring的@Import
//其中用了其扩展点之一的@Import中的Registrar,ArteryResourceImportBeanDefinitionRegistrar才是我们的核心所在
public @interface ArteryResourceScan {
 
String[] value() default {};
 
/**
* 基础包模式
*/
String[] basePackages() default {};
 
/**
* 通配符的模式
*/
String[] typeAliases() default {};
 
/**
* Artery Resource工厂Bean
*
* @return
*/
Class<? extends ArteryResourceFactoryBean> factoryBean() defaultArteryResourceFactoryBean.class;
 
/**
* This property specifies the annotation that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have
* the specified annotation
* </p>
*/
Class<? extends Annotation> annotationClass() default Annotation.class;
 
/**
* This property specifies the parent that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have
* the specified interface class as a parent.
* </p>
*/
Class<?> markerInterface() default Class.class;
 
/**
* The property specifies the beanName gererator will extends parent
*/
 
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
 
/**
* is lazy load,default false
*/
boolean lazy() default false;
 
/**
* scope default singleton
*/
String scope() default AbstractBeanDefinition.SCOPE_SINGLETON;
 
}

定义我的数据层Resource使用的注解

如何利用Spring的@Import扩展点与spring进行无缝整合  2

ArteryResourceImportBeanDefinitionRegistrar实现

我们的注册类实现了spring的即可ImportBeanDefinitionRegistrar,而ImportBeanDefinitionRegistrar提供了一个方法registerBeanDefinitions可以得到我们的ArteryResourceScan 注解,从而定义自己的扫描规则,使用spring的的扫描逻辑帮助我们完成扫描,然后注册到bdmap里面,因我们的Resource层是接口,而spring实例化是不能实例化接口的,所以当spring帮我们扫描成bd的时候,我们这个时候要这个扫描的列表取出来,替换我们的接口类,怎么替换呢?
因为接口需要被代理出去,而代理类帮我们完成我们想要做的事情,比如数据查询,所以我们还需要定义一个工厂bean即FactoryBean,它来帮我们产生对象,FactoryBean也是一个Bean,但是他比较特殊,它可以产生对象,我们先看Registrar的实现:

public class ArteryResourceImportBeanDefinitionRegistrar implementsImportBeanDefinitionRegistrar {
 
private final String DEFAULT_RESOURACE_PATTERN = "**/*.class";
 
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
 
ClassPathArteryResourceScanner scanner = new ClassPathArteryResourceScanner(registry);
/**
* 拿到注解信息
*/
AnnotationAttributes annoAttrs =AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ArteryResourceScan.class.getName()));
 
Class<? extends ArteryResourceFactoryBean> factoryBean = annoAttrs.getClass("factoryBean");
if (!ArteryResourceFactoryBean.class.equals(factoryBean)) {
scanner.setArteryResourceFactoryBean(BeanUtils.instantiateClass(factoryBean));
}
Class<? extends Annotation> annotionClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotionClass)) {
scanner.setAnnotationClass(annotionClass);
}
Class<?> markerInteface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInteface)) {
scanner.setMarkerInterface(markerInteface);
}
 
Class<? extends BeanNameGenerator> nameGenerator = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(nameGenerator)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(nameGenerator));
}
scanner.setLazy(annoAttrs.getBoolean("lazy"));
scanner.setResourceScope(annoAttrs.getString("scope"));
//base package handler
List<String> basePackages = new ArrayList<>(20);
Arrays.asList(annoAttrs.getStringArray("basePackages")).forEach(item -> {
if (StringUtils.hasText(item)) {
basePackages.add(item);
}
});
Arrays.asList(annoAttrs.getStringArray("value")).forEach(item -> {
if (StringUtils.hasText(item)) {
basePackages.add(item);
}
});
 
 
/**
* 处理通配符的扫描问题
*/
String[] typeAlis = annoAttrs.getStringArray("typeAliases");
if (typeAlis != null && typeAlis.length > 0) {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = newCachingMetadataReaderFactory(resolver);
//typeAliaes
for (String typeAliases : Arrays.asList(annoAttrs.getStringArray("typeAliases"))) {
getResource(resolver, metadataReaderFactory, typeAliases, basePackages);
}
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
 
private void getResource(ResourcePatternResolver resolver, MetadataReaderFactory metadataReaderFactory, String classPath, List<String> basePackages) {
classPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(classPath) + "/" + DEFAULT_RESOURACE_PATTERN;
try {
Resource[] resources = resolver.getResources(classPath);
if (resources != null && resources.length > 0) {
MetadataReader metadataReader = null;
for (Resource resource : resources) {
if (resource.isReadable()) {
metadataReader = metadataReaderFactory.getMetadataReader(resource);
basePackages.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
}
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}

以上代码的doScan方法是核心,是调用了我们自定义的扫描类

自定义扫描类ClassPathArteryResourceScanner

public class ClassPathArteryResourceScanner extends ClassPathBeanDefinitionScanner {
private final Logger logger =LoggerFactory.getLogger(ClassPathArteryResourceScanner.class);
/**
* factory baen instance
*/
private ArteryResourceFactoryBean<?> arteryResourceFactoryBean = newArteryResourceFactoryBean<Object>();
/**
* scanner class
*/
private Class<? extends Annotation> annotationClass;
/**
* scanner class
*/
private Class<?> markerInterface;
/**
* is lazy load ,default false
*/
private boolean isLazy = false;
/**
* scope is cantains singleton and prototype,default singlton
*/
private String resourceScope = AbstractBeanDefinition.SCOPE_SINGLETON;
 
 
/**
* 调用父类的构造,构造出扫描对象
*
* @param registry
*/
public ClassPathArteryResourceScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
 
public void registerFilters() {
//是否允许所有的所有的接口(预留)
boolean acceptAllIntefaces = true;
 
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllIntefaces = false;
}
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface));
acceptAllIntefaces = false;
}
if (acceptAllIntefaces) {
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
 
}
 
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
 
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (!beanDefinitions.isEmpty()) {
//SPRING 扫描到每个加了@Component或者@Service 成BD
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
 
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
beanDefinitions.forEach(this::processBeanDefinition);
}
 
private void processBeanDefinition(BeanDefinitionHolder holder) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating ArteryResourceBean with name {} and {} mapperInterfaces", holder.getBeanName(), definition.getBeanClassName());
}
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.arteryResourceFactoryBean.getClass());
definition.setLazyInit(isLazy);//延迟加载
definition.setScope(resourceScope);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
 
 
@Override
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throwsIllegalStateException {
// check is sucess
boolean isSucc = true;
if (super.checkCandidate(beanName, beanDefinition)) {
isSucc = true;
} else {
isSucc = false;
}
return isSucc;
}
 
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
boolean isComponent = false;
String beanClassName = beanDefinition.getMetadata().getClassName();
isComponent = beanClassName.endsWith("Resource");
try {
 
isComponent = isComponent ?Class.forName(beanDefinition.getMetadata().getClassName()).isAnnotationPresent(ArteryDao.class): false;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (isComponent) {
isComponent = beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
return isComponent;
}
 
public void setArteryResourceFactoryBean(ArteryResourceFactoryBean<?> arteryResourceFactoryBean) {
this.arteryResourceFactoryBean = arteryResourceFactoryBean;
}
 
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
 
public void setLazy(boolean lazy) {
isLazy = lazy;
}
 
public void setResourceScope(String resourceScope) {
this.resourceScope = resourceScope;
}
 
public void setMarkerInterface(Class<?> markerInterface) {
this.markerInterface = markerInterface;
}
}

processBeanDefinition这个方法里面就拿到spring给我们扫描返回的bd,我们循环这个bd
然后替换我们的Resource接口,这里用的是一个FactoryBean
而这个工厂Bean里面的getObject是返回了一个代理对象,具体看下面代码:

如何利用Spring的@Import扩展点与spring进行无缝整合  2

代理注册工厂ResourceRegistry

如何利用Spring的@Import扩展点与spring进行无缝整合  2

它的作用主要是来管理我们的注册工厂

Resouce的代理工厂

如何利用Spring的@Import扩展点与spring进行无缝整合  2

它来管理我们的Resouce工厂,从这个Resource工厂中产生代理类,也就是我们的代理类都在代理工厂中产生,然后我们调用的时候是通过它来产生的一个proxy

真正的代理类

public class ResourceProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = 1L;
private final Logger logger = LoggerFactory.getLogger(ResourceProxy.class);
 
private final Class<T> resourceInterface;
private final Map<Method, ResourceMethod> cacheMethod;
 
 
public ResourceProxy(Class<T> resourceInterface, Map<Method, ResourceMethod> cacheMethod) {
this.resourceInterface = resourceInterface;
this.cacheMethod = cacheMethod;
}
 
 
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
/**
* handler tostring method
*/
if (Object.class.equals(method.getDeclaringClass())) {
result = method.invoke(this, args);
} else if (isDefaultMethod(method)) {
result = invokeDefaultMethod(proxy, method, args);
} else {
/**
* user invoke handler
*/
if (logger.isDebugEnabled()) {
logger.debug("ResourceProxy.invoke begin ....");
logger.debug("================================================");
logger.debug("invoke interface name={}", proxy.getClass().getInterfaces()[0].getName());
logger.debug("invoke method name={}", method.getName());
logger.debug("invoke method args={}", args);
}
ResourceMethod resourceMethod = getCacheMethod(method);
result = resourceMethod.execute(args);
if (logger.isDebugEnabled()) {
logger.debug("ResourceProxy.invoke end ....");
logger.debug("================================================");
logger.debug("invoke method result={}", result);
}
}
return result;
}
 
public ResourceMethod getCacheMethod(Method method) {
ResourceMethod resourceMethod = cacheMethod.get(method);
if (resourceMethod == null) {
resourceMethod = new ResourceMethod(method, resourceInterface);
cacheMethod.put(method, resourceMethod);
}
return resourceMethod;
}
 
 
/**
* invoke default method
*
* @param proxy proxy object
* @param method proxy invoke method
* @param args method args
* @return
* @throws Throwable
*/
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throwsThrowable {
final Constructor<MethodHandles.Lookup> constructor =MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
 
/**
* check is default method
*
* @param method
* @return
*/
private boolean isDefaultMethod(Method method) {
return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC))== Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
}
}

方法调用类

public class ResourceMethod<T> extends AbstractAnnotaionHandlerResource<T> {
 
private final Method method;
 
private final Class<?> resourceInterface;
 
 
public ResourceMethod(Method method, Class<?> resourceInterface) {
this.method = method;
this.resourceInterface = resourceInterface;
}
 
public Object execute(Object[] args) throws Exception {
Object result = null;
initTemplate();
if (method.isAnnotationPresent(PubHandler.class)) {
result = pubHandler(args);
} else {
result = invokeExtMethod(args);
}
return result;
}
 
private void initTemplate(){
if(queryTemplate == null){
queryTemplate =SpringContainerApplicationContext.getInstance().getBean("queryTemplate");
}
 
if(updateTemplate == null){
updateTemplate =SpringContainerApplicationContext.getInstance().getBean("updateTemplate");
 
}
}
 
private Object invokeExtMethod(Object[] args) throws Exception {
Object result = null;
Annotation[] annotations = method.getAnnotations();
if (annotations != null && annotations.length > 0) {
String annotationName = annotations[0].annotationType().getSimpleName();
SqlCommandType type = SqlCommandType.valueOf(annotationName);
switch (type) {
case SELECT:
result = executeQuery(method.getAnnotation(SELECT.class), args);
break;
case INSERT:
result = executeInsert(method.getAnnotation(INSERT.class), args);
break;
case DELETE:
result = executeDelete(method.getAnnotation(DELETE.class), args);
break;
case UPDATE:
result = executeUpdate(method.getAnnotation(UPDATE.class), args);
break;
}
}
return result;
}
 
 
private Object pubHandler(Object[] args) {
Object result = null;
PubHandler ph = method.getAnnotation(PubHandler.class);
switch (ph.handlerType()) {
case P_Q_PAGING:
result = P_Q_PAGING(args);
break;
case L_Q_PAGING:
result = L_Q_PAGING(args);
break;
case I_PERSISTENCE_IN:
result = I_PERSISTENCE_IN(args);
break;
case I_PERSISTENCE_UP:
result = I_PERSISTENCE_UP(args);
break;
case I_PERSISTENCE_UP_OVERRIDE:
result = I_PERSISTENCE_UP_OVERRIDE(args);
break;
case I_PSERSISTENCE_DE:
result = I_PSERSISTENCE_DE(args);
break;
case I_PSERSISTENCE_DE_OVERRIDE:
result = I_PSERSISTENCE_DE_OVERRIDE(args);
break;
case E_Q_GET:
result = E_Q_GET(args);
break;
case L_Q_ENTITY:
result = L_Q_ENTITY(args);
break;
case L_Q_ENTITY_OVERRIDE:
result = L_Q_ENTITY_OVERRIDE(args);
break;
case I_BATCH_PERSISTENCE:
result = I_BATCH_PERSISTENCE(args);
break;
case I_COUNT:
result = I_COUNT(args);
break;
}
return result;
}
@Override
public Method getMethod() {
return method;
}
 
@Override
public Class<?> targetInterface() {
return resourceInterface;
}

从第七步调用直接到了这里,其中1和2是不一样的,哪里不一样呢?因为pubHandler是我们在基类里面封装的curd操作,如果是调用基类,那么会直接将请求代理给我的基类去做,如果是通过注解sql的那么就执行invokeExtMethod

如何利用Spring的@Import扩展点与spring进行无缝整合  2
如何利用Spring的@Import扩展点与spring进行无缝整合  2

最后我们编写一个工厂来获取我们的Bean

AbstractBeanDefinitionFactory

public abstract class AbstractBeanDefinitionFactory extendsAnnotationConfigDefinitionApplicationContext
implements BeanDefinitionFactory {
 
@SuppressWarnings("unchecked")
@Override
public <K> K getBean(String beanName) {
check();
DefinitionBeanFactory<K> beanfactory = () -> {
Object beanInstance = super.applicationContxt.getBean(beanName);
if (beanInstance != null) {
return (K) beanInstance;
} else {
throw new RuntimeException(" get spring ioc instance is null ");
}
};
return beanfactory.getBeanObject();
}
 
@Override
public <K> K getBean(Class<K> kclzz) {
check();
DefinitionBeanFactory<K> beanfactory = () -> super.applicationContxt.getBean(kclzz);
return beanfactory.getBeanObject();
}
private void check() {
if(!super.runStatus) {
throw new RuntimeException("spring 容器未运行");
}
}
 
@Override
public <K> K getBean(String beanName, Class<K> kClass) {
check();
DefinitionBeanFactory<K> beanFactory = () ->super.applicationContxt.getBean(beanName,kClass);
return null;
}
}

我们编写测试,来启动我们的spring容器

如何利用Spring的@Import扩展点与spring进行无缝整合  2
如何利用Spring的@Import扩展点与spring进行无缝整合  2

如何利用Spring的@Import扩展点与spring进行无缝整合  2

以上我只是提供了一个spring扩展的思路,上面截图和代码都不全,因为涉及到公司机密性,我没有办法暴露太多东西在上面,所以有兴趣可以一起交流交流;整合spring的这个框架是我自己编写,没有任何人参与进来,所以我希望如果有这方面兴趣的朋友可以一起交流交流

类图

我的启动类的之间关系(全部是自己的类,不是spring的类):

如何利用Spring的@Import扩展点与spring进行无缝整合  2

代理工厂调用的调用的方法处理继承关系:

如何利用Spring的@Import扩展点与spring进行无缝整合  2

转载请注明来源:如何利用Spring的@Import扩展点与spring进行无缝整合

本文永久链接地址:https://www.adminjie.com/post/3516.html

免责声明:
本站所发布的一切资源仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

附:
二○○二年一月一日《计算机软件保护条例》第十七条规定:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬!鉴于此,也希望大家按此说明研究软件!

版权声明:
一、本站致力于为软件爱好者提供国内外软件开发技术和软件共享,着力为用户提供优资资源。
二、本站提供的部分源码下载文件为网络共享资源,请于下载后的24小时内删除。如需体验更多乐趣,还请支持正版。
三、我站提供用户下载的所有内容均转自互联网。如有内容侵犯您的版权或其他利益的,若有侵犯你的权益请:点此维权 站长会进行审查之后,情况属实的会在三个工作日内为您删除。

  • 代码之家会员升级
  • 最新文章
  • 阿里云大优惠
  • 热门文章