mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-02 11:17:40 +08:00
Scan screen classes using ASM without loading classes eagerly
This commit is contained in:
parent
8e49e663af
commit
bebd89b070
@ -30,8 +30,8 @@ import com.haulmont.cuba.gui.screen.Screen;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import com.haulmont.cuba.gui.screen.UiDescriptor;
|
||||
import com.haulmont.cuba.gui.sys.ScreenDescriptorUtils;
|
||||
import com.haulmont.cuba.gui.sys.ScreensConfiguration;
|
||||
import com.haulmont.cuba.gui.sys.ScreensConfiguration.UiControllerDefinition;
|
||||
import com.haulmont.cuba.gui.sys.UiControllerDefinition;
|
||||
import com.haulmont.cuba.gui.sys.UiControllersConfiguration;
|
||||
import com.haulmont.cuba.gui.xml.layout.ScreenXmlLoader;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -78,7 +78,7 @@ public class WindowConfig {
|
||||
@Inject
|
||||
protected Metadata metadata;
|
||||
@Inject
|
||||
protected List<ScreensConfiguration> screensConfigurations;
|
||||
protected List<UiControllersConfiguration> configurations;
|
||||
@Inject
|
||||
protected ScreenXmlLoader screenXmlLoader;
|
||||
|
||||
@ -221,7 +221,7 @@ public class WindowConfig {
|
||||
}
|
||||
|
||||
protected void loadScreenConfigurations() {
|
||||
for (ScreensConfiguration provider : screensConfigurations) {
|
||||
for (UiControllersConfiguration provider : configurations) {
|
||||
List<UiControllerDefinition> uiControllers = provider.getUIControllers();
|
||||
|
||||
for (UiControllerDefinition definition : uiControllers) {
|
||||
|
@ -12,10 +12,13 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface UiController {
|
||||
@AliasFor("id")
|
||||
String ID_ATTRIBUTE = "id";
|
||||
String VALUE_ATTRIBUTE = "value";
|
||||
|
||||
@AliasFor(ID_ATTRIBUTE)
|
||||
String value() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
@AliasFor(VALUE_ATTRIBUTE)
|
||||
String id() default "";
|
||||
|
||||
// todo move to separate annotation
|
||||
|
@ -3,9 +3,9 @@ package com.haulmont.cuba.gui.sys;
|
||||
import com.google.common.base.Strings;
|
||||
import com.haulmont.cuba.core.global.DevelopmentException;
|
||||
import com.haulmont.cuba.gui.screen.Screen;
|
||||
import com.haulmont.cuba.gui.screen.UiDescriptor;
|
||||
import com.haulmont.cuba.gui.screen.Subscribe;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import com.haulmont.cuba.gui.screen.UiDescriptor;
|
||||
|
||||
import static com.haulmont.bali.util.Preconditions.checkNotNullArgument;
|
||||
|
||||
@ -16,13 +16,23 @@ public final class ScreenDescriptorUtils {
|
||||
|
||||
public static String getInferredScreenId(UiController uiController, Class<? extends Screen> annotatedScreenClass) {
|
||||
checkNotNullArgument(uiController);
|
||||
checkNotNullArgument(annotatedScreenClass);
|
||||
|
||||
String id = uiController.value();
|
||||
return getInferredScreenId(uiController.id(), uiController.value(), annotatedScreenClass.getName());
|
||||
}
|
||||
|
||||
public static String getInferredScreenId(String idAttribute, String valueAttribute, String className) {
|
||||
String id = valueAttribute;
|
||||
if (Strings.isNullOrEmpty(id)) {
|
||||
id = uiController.id();
|
||||
id = idAttribute;
|
||||
|
||||
if (Strings.isNullOrEmpty(id)) {
|
||||
throw new DevelopmentException("Screen class annotated with @UiController without id " + annotatedScreenClass);
|
||||
int indexOfDot = className.lastIndexOf('.');
|
||||
if (indexOfDot < 0) {
|
||||
id = className;
|
||||
} else {
|
||||
id = className.substring(indexOfDot + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,100 +0,0 @@
|
||||
package com.haulmont.cuba.gui.sys;
|
||||
|
||||
import com.haulmont.cuba.core.global.Scripting;
|
||||
import com.haulmont.cuba.gui.screen.Screen;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Configuration that performs ClassPath scanning of {@link UiController}s and provides {@link UiControllerDefinition}.
|
||||
*/
|
||||
public class ScreensConfiguration {
|
||||
private static final Logger log = LoggerFactory.getLogger(ScreensConfiguration.class);
|
||||
|
||||
@Inject
|
||||
protected Scripting scripting;
|
||||
|
||||
protected List<String> packages;
|
||||
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
// todo add explicit exports
|
||||
|
||||
public ScreensConfiguration() {
|
||||
}
|
||||
|
||||
public List<String> getPackages() {
|
||||
return packages;
|
||||
}
|
||||
|
||||
public void setPackages(List<String> packages) {
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public List<UiControllerDefinition> getUIControllers() {
|
||||
ClassPathScanningCandidateComponentProvider provider = createComponentScanner();
|
||||
|
||||
log.trace("Scanning packages {}", packages);
|
||||
|
||||
return packages.stream()
|
||||
.flatMap(scanPackage -> provider.findCandidateComponents(scanPackage).stream())
|
||||
.map(BeanDefinition::getBeanClassName)
|
||||
.map(className -> {
|
||||
log.trace("Found screen controller {}", className);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Screen> screenClass = (Class<? extends Screen>) scripting.loadClassNN(className);
|
||||
|
||||
UiController uiController = screenClass.getAnnotation(UiController.class);
|
||||
if (uiController == null) {
|
||||
throw new RuntimeException("Screen class does not have @UiController annotation : " + screenClass);
|
||||
}
|
||||
|
||||
String id = ScreenDescriptorUtils.getInferredScreenId(uiController, screenClass);
|
||||
|
||||
return new UiControllerDefinition(id, className);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected ClassPathScanningCandidateComponentProvider createComponentScanner() {
|
||||
// Don't pull default filters (@Component, etc.):
|
||||
ClassPathScanningCandidateComponentProvider provider
|
||||
= new ClassPathScanningCandidateComponentProvider(false);
|
||||
provider.setResourceLoader(getResourceLoader());
|
||||
provider.addIncludeFilter(new AnnotationTypeFilter(UiController.class));
|
||||
return provider;
|
||||
}
|
||||
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public final static class UiControllerDefinition {
|
||||
private final String id;
|
||||
private final String controllerClass;
|
||||
|
||||
public UiControllerDefinition(String id, String controllerClass) {
|
||||
this.id = id;
|
||||
this.controllerClass = controllerClass;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getControllerClass() {
|
||||
return controllerClass;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.sys;
|
||||
|
||||
public final class UiControllerDefinition {
|
||||
private final String id;
|
||||
private final String controllerClass;
|
||||
|
||||
public UiControllerDefinition(String id, String controllerClass) {
|
||||
this.id = id;
|
||||
this.controllerClass = controllerClass;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getControllerClass() {
|
||||
return controllerClass;
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package com.haulmont.cuba.gui.sys;
|
||||
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Configuration that performs ClassPath scanning of {@link UiController}s and provides {@link UiControllerDefinition}.
|
||||
*/
|
||||
public class UiControllersConfiguration {
|
||||
public static final String DEFAULT_CLASS_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UiControllersConfiguration.class);
|
||||
|
||||
protected ApplicationContext applicationContext;
|
||||
protected MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
protected List<String> packages = Collections.emptyList();
|
||||
protected List<String> classNames = Collections.emptyList();
|
||||
|
||||
public UiControllersConfiguration() {
|
||||
}
|
||||
|
||||
@Inject
|
||||
protected void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.metadataReaderFactory = new CachingMetadataReaderFactory(applicationContext);
|
||||
}
|
||||
|
||||
public List<String> getPackages() {
|
||||
return packages;
|
||||
}
|
||||
|
||||
public void setPackages(List<String> packages) {
|
||||
Preconditions.checkNotNullArgument(packages);
|
||||
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public List<String> getClassNames() {
|
||||
return classNames;
|
||||
}
|
||||
|
||||
public void setClassNames(List<String> classNames) {
|
||||
this.classNames = classNames;
|
||||
}
|
||||
|
||||
public List<UiControllerDefinition> getUIControllers() {
|
||||
log.trace("Scanning packages {}", packages);
|
||||
|
||||
Stream<UiControllerDefinition> scannedControllersStream = packages.stream()
|
||||
.flatMap(this::scanPackage)
|
||||
.filter(this::isCandidateUiController)
|
||||
.map(this::extractControllerDefinition);
|
||||
|
||||
Stream<UiControllerDefinition> explicitControllersStream = classNames.stream()
|
||||
.map(this::loadClassMetadata)
|
||||
.map(this::extractControllerDefinition);
|
||||
|
||||
return Stream.concat(scannedControllersStream, explicitControllersStream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected MetadataReader loadClassMetadata(String className) {
|
||||
Resource resource = getResourceLoader().getResource(className);
|
||||
if (!resource.isReadable()) {
|
||||
throw new RuntimeException(String.format("Resource %s is not readable for class %s", resource, className));
|
||||
}
|
||||
try {
|
||||
return getMetadataReaderFactory().getMetadataReader(resource);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to read resource " + resource, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected UiControllerDefinition extractControllerDefinition(MetadataReader metadataReader) {
|
||||
Map<String, Object> uiControllerAnn =
|
||||
metadataReader.getAnnotationMetadata().getAnnotationAttributes(UiController.class.getName());
|
||||
|
||||
String idAttr = null;
|
||||
String valueAttr = null;
|
||||
if (uiControllerAnn != null) {
|
||||
idAttr = (String) uiControllerAnn.get(UiController.ID_ATTRIBUTE);
|
||||
valueAttr = (String) uiControllerAnn.get(UiController.VALUE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
String className = metadataReader.getClassMetadata().getClassName();
|
||||
String controllerId = ScreenDescriptorUtils.getInferredScreenId(idAttr, valueAttr, className);
|
||||
|
||||
return new UiControllerDefinition(controllerId, className);
|
||||
}
|
||||
|
||||
protected Stream<MetadataReader> scanPackage(String packageName) {
|
||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
resolveBasePackage(packageName) + '/' + DEFAULT_CLASS_RESOURCE_PATTERN;
|
||||
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
Resource[] resources;
|
||||
try {
|
||||
resources = resourcePatternResolver.getResources(packageSearchPath);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to scan package " + packageName, e);
|
||||
}
|
||||
|
||||
return Arrays.stream(resources)
|
||||
.peek(resource -> log.trace("Scanning {}", resource))
|
||||
.filter(Resource::isReadable)
|
||||
.map(resource -> {
|
||||
try {
|
||||
return getMetadataReaderFactory().getMetadataReader(resource);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to read resource " + resource, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected MetadataReaderFactory getMetadataReaderFactory() {
|
||||
return metadataReaderFactory;
|
||||
}
|
||||
|
||||
protected String resolveBasePackage(String basePackage) {
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
return ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(basePackage));
|
||||
}
|
||||
|
||||
protected boolean isCandidateUiController(MetadataReader metadataReader) {
|
||||
return metadataReader.getClassMetadata().isConcrete()
|
||||
&& metadataReader.getAnnotationMetadata().hasAnnotation(UiController.class.getName());
|
||||
}
|
||||
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
@ -63,8 +63,7 @@
|
||||
|
||||
<!-- todo custom XSD for screen configuration: scanning/defining screens -->
|
||||
|
||||
<bean id="cuba_web_ScreensConfiguration"
|
||||
class="com.haulmont.cuba.gui.sys.ScreensConfiguration">
|
||||
<bean id="cuba_web_UiControllersConfiguration" class="com.haulmont.cuba.gui.sys.UiControllersConfiguration">
|
||||
<property name="packages">
|
||||
<list>
|
||||
<value>com.haulmont.cuba.gui.app</value>
|
||||
|
Loading…
Reference in New Issue
Block a user