2.8.5 Java Annotation Configuration
hasor-config provides optional annotation configuration on top of hasor-core; applications do not have to implement Module. Use the matching dependency version:
<dependency>
<groupId>net.hasor</groupId>
<artifactId>hasor-config</artifactId>
<version>5.3.0</version>
</dependency>
Starting an application
package com.example;
import net.hasor.config.ApplicationBoot;
import net.hasor.config.Bean;
import net.hasor.config.Configuration;
@Configuration
public class Application {
public static void main(String[] args) {
ApplicationBoot.run(Application.class, args);
}
@Bean
public Greeting greeting() {
return new Greeting();
}
}
Place the business class in a separate Greeting.java file:
package com.example;
public class Greeting {
public String message() {
return "Hello Hasor";
}
}
ApplicationBoot requires the primary class to have @Configuration and registers it as a primarySource, but does not infer scan packages. create(...) returns a Hasor builder; run(...) binds arguments and creates AppContext. The scan scope is configured exclusively through hasor.loadPackages.
Existing Module-based applications can also install configuration explicitly:
import net.hasor.config.ConfigurationModule;
apiBinder.installModule(ConfigurationModule.of(Application.class));
// Or assemble using the scan scope configured in Core:
apiBinder.installModule(ConfigurationModule.auto());
Bean factories and lifecycle
Annotate methods declared by the configuration class with @Bean to register Beans by return type. Method parameters are resolved by type by default and can express dependencies between Beans. Use @Inject("name") on a parameter to resolve by name and type, or @Inject(value = "beanId", byType = Type.ByID) to resolve by binding ID. An empty @Inject.value still resolves by type.
@Bean
public OrderService orderService(DataSource mainSource,
@Inject("orders") DataSource ordersSource) {
return new OrderService(mainSource, ordersSource);
}
Dependencies are resolved when the factory method is called, so they may be registered after the factory. Each factory invocation resolves its parameters again, respecting each dependency's scope.
- By default,
singleton = truecreates the bean and runs its configuredinitMethodduring container startup;singleton = falseuses prototype scope and creates an instance when requested. - If
valueis omitted, the method name becomes the binding ID; explicit@Bean("name")sets both the binding ID and name. - Methods cannot be
static, abstract, or returnvoid; the actual return value must not benull. initMethodruns after the factory creates an object.destroyMethodruns on created and tracked objects when AppContext closes. Both must be public no-argument methods.- Configuration classes are not enhanced with proxies that intercept internal method calls. Calling another
@Beanmethod directly is still an ordinary Java call. Use factory method parameter injection when you need the container-managed object.
Configuration classes can also implement Module or WebMvcConfigurer to participate in module configuration and lifecycle. Field injection is not complete during module configuration; do not depend on injected business objects in callbacks such as loadModule.
Automatic scanning
Adding Config automatically installs ConfigurationModule through the default module; no duplicate installation is needed.
auto() and the public no-argument constructor scan the Core scope. of(...) handles only explicitly supplied configuration classes and does not also scan routes.
Subclasses can specify configuration classes through the protected constructor super(Application.class).
<config xmlns="https://www.hasor.net/sechma/main">
<hasor.loadPackages>com.example.*</hasor.loadPackages>
</config>
Separate multiple packages with commas; the environment variable is HASOR_LOAD_PACKAGES. The startup class package is not automatically included in the scan scope.
The Config-, Web-, and Boot-specific scanPackages and autoScan settings and environment variables are no longer read.
An empty scope performs no scanning. Scanner traverses the classpath once, processing configuration classes and Beans before Web routes; route processing is skipped when Web dependencies are absent.
Web controllers and MVC configuration
Web features require hasor-web or an embedded container module that depends on it transitively. hasor-config does not add a Web runtime automatically. Hasor must be created with a ServletContext; see Web Launcher for an embedded server example.
Web auto-configuration finds controllers with @MappingTo on their class or methods within the configured scope and registers routes. It obtains Controller Providers during configuration and resolves instances through them at request time. Controllers use Hasor dependency injection without individual loadMappingTo calls. Method mappings work without a class mapping; when both are present, class paths supply common prefixes. See Request Handling. Root mappings such as @MappingTo("/") are also supported.
A configuration class can implement WebMvcConfigurer:
package com.example;
import net.hasor.config.Configuration;
import net.hasor.config.web.cors.CorsRegistry;
import net.hasor.config.web.render.JsonRenderConfigurer;
import net.hasor.web.WebApiBinder;
import net.hasor.cobble.loader.providers.PrefixResourceLoader;
import net.hasor.config.web.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(WebApiBinder binder) {
binder.addResource("/assets/**", new PrefixResourceLoader(binder.getResourceLoader(), "web-assets"))
.welcomeFile("home.html")
.order(-100);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://hasor.net")
.allowedMethods("GET", "POST")
.allowedHeaders("content-type", "authorization")
.allowCredentials(true)
.maxAge(1800);
}
@Override
public void configureJson(JsonRenderConfigurer configurer) {
configurer.renderEngine(net.hasor.web.render.json.JsonRenderEngine.class);
}
}
Starting with 5.3.0, register MVC interceptors through binder.bindInterceptor(...) inside WebMvcConfigurer.addInterceptors(WebApiBinder). Registration order determines execution order; see MVC interceptors.
Configuration classes can also annotate factories returning ExceptionHandler with @Exception to register the handled exception types automatically. These factories reuse the Bean creation rules above, including parameter injection, @DependsOn, and scope and lifecycle settings through @Bean. See exception handling.
Annotation-based AOP
hasor-core installs net.hasor.core.aop.AopModule by default, supporting net.hasor.cobble.dynamic.Aop without a Config dependency. Class-level interceptors run before method-level interceptors. Programmatic AOP through ApiBinder.bindInterceptor remains a Core feature; see Class-Level Interceptors.
Factory methods can declare explicit initialization dependencies with @DependsOn; see initialization dependencies.