Spring #EnableSpringConfigured not work - java

I try to use #EnableSpringConfigured, but it is not work.
Engine.java
#Component
public class Engine {
public void run() {
System.out.println("Engine run");
}
}
Entity.java
#Component
#Configurable(autowire = Autowire.BY_TYPE)
public class Entity {
#Autowired
private Engine engine;
public void exec() {
if (engine != null)
engine.run();
else
System.out.println("exec failure");
}
}
EntityBuilder.java
#Component
public class EntityBuilder {
public Entity create() {
return new Entity();
}
}
EntityApplication.java
#Configuration
#ComponentScan
#EnableSpringConfigured
public class EntityApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EntityApplication.class);
EntityBuilder builder = context.getBean(EntityBuilder.class);
builder.create().exec();
}
}
The above four java in same package, I try run EntityApplication.java and expect to see "Engine run", but actual result always is "exec failure".
help! code is in https://github.com/lemonguge/spring/tree/master/spring-core/spring-aspect/src/main/java/cn/homjie/spring/aspect/newx

Spring can configure objects only if load time waving is enabled. You should mark your configuration with #EnableLoadTimeWeaving annotation
You need either load-time weaving or compile time weaving for the Configurable annotation

You declared Entity as a spring component, but inside EntityBuilder create()method, you manually creating Entity instance which means you
are not using Entity object which is created by spring container.
So how can spring auto wires Engine instance into your own Entity instance.
Reason why every time if (engine != null) becoming false.
If you declared your classes as spring components, spring container creates
the instances for them and we need to us them inorder to get the proper wiring system. Dont create your own instances if those classes connected with wiring.

Related

Spring Boot - Property placeholders remain unresolved despite setting values with System.setProperty

I have a bean with a constructor as follows. The password argument is resolved from the placeholder my.password, with a default value of DEFAULT. If the value of DEFAULT is passed, a warning is logged. Note - this Bean is contained within an imported third-party library.
#Bean
public class EncryptionBean {
public EncryptionBean(#Value("${my.password}") String password) {
if "DEFAULT".equals(password) {
// log warning message
} else {
// do stuff with the password
}
}
}
The password is retrieved at startup from an external system using a client SDK. This SDK object is itself provided as a Bean (also from a third-party library). After retrieving the password, I am setting it as a System property for the above EncryptionBean to have access to at the time of instantiation:
#Configuration
public class MyConfiguration {
#Autowired
public SDKObject sdkObject;
#PostConstruct
public void init() {
System.setProperty("my.password", sdkObject.retrievePassword());
// #Value("${my.password"}) should now be resolvable when EncryptionBean is instantiated
}
}
However, EncryptionBean is still being instantiated with a value of DEFAULT for my.password. I'm wondering if System.setProperty in #PostConstruct might be getting executed AFTER Spring has already instantiated the instance of EncryptionBean?
If so, is there a way to guarantee this property has been set before Spring instantiates EncryptionBean? I came across #DependsOn as a way to control the order Beans get instantiated by Spring, but since EncryptionBean comes from a third-party library, I haven't been able to make this annotation work.
Instead of setting a system property, you should create a Spring EnvironmentPostProcessor class to retrieve the password from the external source and add it to the Spring Environment. That would look something like this:
public class PasswordEnvironmentPostProcessor implements EnvironmentPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
SDKObject sdkObject = applicationContext.getBean(SDKObject.class);
Map<String, Object> properties = Collections.singletonMap("my.password", sdkObject.retrievePassword());
MapPropertySource propertySource = new MapPropertySource("password", properties);
environment.getPropertySources().addFirst(propertySource);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Then you'll need to register this class with Spring by adding an entry to the file META-INF/spring.factories that looks like this:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.PaswordEnvironmentPostProcessor
Documentation for this is available here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.application.customize-the-environment-or-application-context.
I could not figure out a nice clean way to inject a property at runtime without a lot of boilerplate. But I came up with a clean way to do what you want to do without having to refresh the application context or messing with the 3rd party library implementation.
First we exclude the 3rd party bean from our application context:
#ComponentScan(excludeFilters = #ComponentScan.Filter(value = EncryptionBean.class, type = FilterType.ASSIGNABLE_TYPE))
#SpringBootApplication
public class SandboxApplication {
public static void main(String[] args) {
SpringApplication.run(SandboxApplication.class, args);
}
}
Then we create the Bean ourselves with the values we want.
#Configuration
public class MyConfiguration {
public final SDKObject sdkObject;
public MyConfiguration(SDKObject sdkObject) {
this.sdkObject = sdkObject;
}
#Bean
public EncryptionBean encryptionBean() {
return new EncryptionBean(sdkObject.retrievePassword());
}
}

spring boot - integration test autowired interface no such bean found

I have a spring-boot app that now needs to support multiple Object stores and selectively use the desired store based on the environment. Essentially what i have done is create an interface that each store repository then implements.
I have simplified the code for the examples.
I have created 2 beans for each store type based on the spring profile determining the env:
#Profile("env1")
#Bean
public store1Sdk buildClientStore1() {
return new store1sdk();
}
#Profile("env2")
#Bean
public store2Sdk buildClientStore2() {
return new store2sdk();
}
in the service layer I have autowired the interface and then in the repositories i have used #Profile to specify which instance of the interface to use.
public interface ObjectStore {
String download(String fileObjectKey);
...
}
#Service
public class ObjectHandlerService {
#Autowired
private ObjectStore objectStore;
public String getObject(String fileObjectKey) {
return objectStore.download(fileObjectKey);
}
...
}
#Repository
#Profile("env1")
public class Store1Repository implements ObjectStore {
#Autowired
private Store1Sdk store1client;
public String download(String fileObjectKey) {
return store1client.getObject(storeName, fileObjectKey);
}
}
When I start the application with the configured "env" this actually runs as expected. however when running the test I get the "no qualifying bean of type ObjectStore. expected at least 1 bean which qualifies as autowire candidate."
#ExtendWith({ SpringExtension.class })
#SpringBootTest(classes = Application.class)
#ActiveProfiles("env1,test")
public class ComposerServiceTest {
#Autowired
private ObjectHandlerService service;
#Test
void download_success() {
String response = service.getObject("testKey");
...
}
}
As noted in the #ActiveProfile on the test class there are some other environments e.g. dev,test,prod. I have tried playing around with Component scan, having impl and interface in the same package, etc, to no success. I feel like I am missing something obvious with the test setup. But could be something with my overall application config? my main aim with the solution is to avoid having something a long the lines of
if (store1Sdk != null) {
store1Sdk.download(fileObjectKey);
}
if (store2Sdk != null) {
store2Sdk.download(fileObjectKey);
}
Try #ActiveProfiles({"env1", "test"}).
Activate multiple profiles using #ActiveProfiles and specify profiles as an array.
this probrom because Store1Repository use #Profile("env1"), when you use #test,this class not invoke. try delete #Profile("env1") of Store1Repository.
if you use #test, both of store1Sdk/store2Sdk don't instanse, try add default instanse.eg:
#Bean
public store2Sdk buildClientStoreDefault() {
return new store2sdk();
}

Spring Boot - Auto configuration of a class that contains AutoWired dependencies

I am in the process of developing a common java library with reusable logic to interact with some AWS services, that will in turn be used by several consumer applications. For reasons outlined here, and the fact that Spring Boot seems to provide a lot of boilerplate free code for things like SQS integration, I have decided to implement this common library as a custom spring boot starter with auto configuration.
I am also completely new to the Spring framework and as a result, have run into a problem where my auto-configured class's instance variables are not getting initialized via the AutoWired annotation.
To better explain this, here is a very simplified version of my common dependency.
CommonCore.java
#Component
public class CommonCore {
#AutoWired
ReadProperties readProperties;
#AutoWired
SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging
public CommonCore() {
Properties props = readProperties.loadCoreProperties();
//initialize stuff
}
processEvents(){
// starts processing events from a kinesis stream.
}
}
ReadProperties.java
#Component
public class ReadProperties {
#Value("${some.property.from.application.properties}")
private String someProperty;
public Properties loadCoreProperties() {
Properties properties = new Properties();
properties.setProperty("some.property", someProperty);
return properties;
}
}
CoreAutoConfiguration.java
#Configuration
public class CommonCoreAutoConfiguration {
#Bean
public CommonCore getCommonCore() {
return new CommonCore();
}
}
The common dependency will be used by other applications like so:
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class SampleConsumerApp implements ApplicationRunner {
#Autowired
CommonCore commonCore;
public SampleConsumerApp() {
}
public static void main(String[] args) {
SpringApplication.run(SampleConsumerApp.class, args);
}
#Override
public void run(ApplicationArguments args) {
try {
commonCore.processEvents();
} catch (Exception e) {
e.printStackTrace();
}
}
}
The main problem I have like I mentioned, is the AutoWired objects in the CommonCore instance are not getting initialized as expected. However, I think the actual problems are more deeply rooted; but due to my lack of understanding of the Spring framework, I am finding it difficult to debug this on my own.
I am hoping for a few pointers along these points
Does this approach of developing a custom starter make sense for my use case?
What is the reason for the AutoWired dependencies to not get initialized with this approach?
Wild guess, but I think it's because of the order of how things are constructed. I am talking about this class:
#Component
public class CommonCore {
#AutoWired
ReadProperties readProperties;
#AutoWired
SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging
public CommonCore() {
Properties props = readProperties.loadCoreProperties();
//initialize stuff
}
processEvents(){
// starts processing events from a kinesis stream.
}
}
You are trying to use a Spring injected component in a constructor, but constructor is called before Spring can do its #Autowire magic.
So one option is to autowire as a constructor argument
Something like this (untested):
#Component
public class CommonCore {
private final ReadProperties readProperties;
private final SqsListener sqsListener; // this will be based on spring-cloud-starter-aws-messaging
#AutoWired
public CommonCore(SqsListener sqsListener, ReadProperties readProperties) {
this.readProperties = readPropertis;
this.sqsListener = sqsListener;
Properties props = readProperties.loadCoreProperties();
//initialize stuff
}
processEvents(){
// starts processing events from a kinesis stream.
}
}
Sidenote: I prefer to use dependency injection via constructor arguments always, wherever possible. This also makes unit testing a lot easier without any Spring specific testing libraries.

Class based on factory class not available for autowiring

I'm trying to learn the basics of Spring and have defined this class?
public class Processor {
public Processor() {
this.setup();
}
private void setup(){
//run some setup code
}
public String processString(String str) {
// process the string
return str;
}
}
I want to Spring enable this class so I use a factory bean:
Reading https://www.baeldung.com/spring-factorybean I use:
public class Processor implements FactoryBean<Processor> {
#Override
public Processor getObject() throws Exception {
return new Processor();
}
#Override
public Class<?> getObjectType() {
return Processor.class;
}
}
To Test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ProcessorFactory.class)
public class ProcessorTest {
#Autowired
private Processor processor;
#Test
public void testProcessor() {
//Some tests
}
}
This works as expected.
When I try to use
#Autowired
private Processor processor;
elsewhere in my project I receive compile-time error :
Could not autowire. No beans of 'Processor' type found.
Have I not setup the factory correctly? I should annotate the Processor object to indicate it is to be autowired ? Perhaps this is not a valid use case for Factory ?
In general, factory beans are pretty outdated, first spring versions indeed used this approach, but spring has evolved since that time.
The Factory bean itself should be registered in the spring configuration (the very first versions of spring used xml based configuration because Java Annotation did not exist at that time) so the tutorial contains the XML configuration example. Anyway, that's probably the reason of failure. In the test you should also specify the path to the xml configuration otherwise the factory bean won't be loaded.
You can use these factory beans (they're still supported) but they have the following downsides:
They couple your code to the spring framework
A lot of boilerplate (usually in typical application there can be hundreds of beans, so creating a Factory Bean for each one is an overkill).
So you can:
Instead of using Factory Beans, annotate the Processor with #Component annotation (or more specialized #Service).
Alternatively Use Java configuration:
#Configration
public class MyConfig {
#Bean
public Processor processor() {
// optionally slightly customize the creation of the class if required;
return new Processor();
}
}

How to properly overload Spring bean configuration

I'm having two spring(4.2) java configurations, one in a base module and one in a client specific module:
#Configuration
public class BaseConfig {
#Bean
public A getA() {
return new A("aaa");
}
}
#Configuration
public class ClientConfig {
#Bean
public A getA() {
return new A("bbbb");
}
}
During the app load there is always BaseConfig.getA() called, how can I ovverride the base bean factory configuration to have some client specific stuff?
Personally I would NEVER override a bean in spring! I have seen people spend too much time debugging issues related to this. For the same reason I would never use #Primary.
In this case I would have 3 contexts
Context that contains beans unique to the parent context
Context that contains beans unique to the child context
Abstract context that contains all shared beans.
This way you will specify the 2 contexts to load. This could be done programatically, or using profiles. Potentially you will need more contexts, because you probably want some of your beans to be different in tests.
I think that you should take a look at the #Profile annotation. You could simply split configuration into different base and client specific one like:
#Configuration
#Profile("base")
public class BaseConfig {
#Bean
public A getA() {
return new A("aaa");
}
}
#Configuration
#Profile("client")
public class ClientConfig {
#Bean
public A getA() {
return new A("bbbb");
}
}
now run the specific profile by adding
#ActiveProfiles("base") on application main method
spring.profiles.active=base entry in application.properties
or even pass profile name into jvm params
I'm not sure on how to extend bean config classes. One solution is to mark the bean in ClientConfig with #Primary annotation. This will cause the ClientConfig definition of bean A to be used.
If you include both configurations you can check Primary annotation: Primary
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
#Profile Annotation can be used for this...
#Configuration
#Profile("base")
public class BaseConfig {
#Bean
public A getA() {
return new A("aaa");
}
}
#Configuration
#Profile("client")
public class ClientConfig {
#Bean
public A getA() {
return new A("bbbb");
}
}
Use the following link
https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/
This is an answer to a comment above, but since comments have limited formating and size, I will reply with an answer instead.
how does spring define the ordering and overriding of beans when loading configuration files
It depends what you mean by loading multiple configuration. If you have one spring context and have two classes with #Configuration and do a component scan, then Spring will build the dependency tree, and which ever context (bean) is loaded last will define the bean (as it overrides the fist definition).
If you have have multiple Spring contexts in a parent child relation, then the child and see parent beans, and will also 'override' parent beans if you use child.getBean(type.class). The parent can't see bean from children.
Using #Primary. If you have a Spring context (can come from multiple configurations) that defines two beans of the same type, you will not be able to use context.getBean(type.class) or #AutoWired (without #Qualifier) because you have multiple beans of the same type. This behaviour can be altered if one of the beans is #Primary. I try to avoid the use of #Primary in my own code, but I can see it is used heavily used in Spring boots auto configure system, so I think it has some subtle usage when it comes to framework design.
Here is a small example, note that if you load configuration classes directly, they don't need to have the #Configuration annotation.
public class ParentChildContext {
public static void main(String[] args) {
parentChildContext();
twoConfigurationsSameContext();
}
private static void twoConfigurationsSameContext() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Parent.class, Child.class);
// if you have two beans of the same type in a context they can be loaded by name
Object childA = ctx.getBean("childA");
System.out.println("childA = " + childA);
Object parentA = ctx.getBean("parentA");
System.out.println("parentA = " + parentA);
// since both configurations define A, you can't do this
ctx.getBean(A.class);
}
private static void parentChildContext() {
ApplicationContext parentCtx = new AnnotationConfigApplicationContext(Parent.class);
A parentA = parentCtx.getBean(A.class);
System.out.println("parent = " + parentA);
AnnotationConfigApplicationContext childCtx = new AnnotationConfigApplicationContext();
childCtx.register(Child.class);
childCtx.setParent(parentCtx);
childCtx.refresh();
A childA = childCtx.getBean(A.class);
System.out.println("child = " + childA);
}
public static class Parent {
#Bean
//#Primary // if you enable #Primary parent bean will override child unless the context is hierarchical
public A parentA() {
return new A("parent");
}
}
public static class Child {
#Bean
public A childA() {
return new A("child");
}
}
public static class A {
private final String s;
public A(String s) {
this.s = s;
}
#Override
public String toString() {
return "A{s='" + s + "'}";
}
}
}

Categories