consider this example
I have interface
interface FSClient
String getMeData()
and classes implementing it
class MockFSClient implements FSClient {
public String getMeData() {
return “I am mock”;
}
}
class RestFSClient implements FSClient {
public String getMeData() {
final String data = // from web service
return data;
}
}
and a manager which looks like
class FSManager {
private FSClient fsclient;
#Autowired
public void FSManager(#Nonnull final FSClient fsClient) {
this.fsclient = fsClient;
}
}
I want to instantiate fsclient based on a system property
com.fs.mock=true
meaning if com.fs.mock=true, fsclient should be MockFSClient else fsClient should be RestFSClient
How can I do that?
Why do I need it?
so that I can decouple and do testing
Please help, I am new to Spring
Are you asking how you get the value of com.fs.mock because the answer is use the #Value annotation and a PropertyPlaceholderConfigurer bean.
If you are asking how do you create the actual object then as #Jakkra says use a factory that contains an if statement around the value of com.fs.mock. Its not the most elegant solution but it would work.
Example
public class ClientFactory {
#Value("${com.fs.mock}")
private boolean mockFlag;
public static returnClientInstance(){
if(mockFlag){
return new MockFSClient();
}
else{
return new RestFSClient();
}
}
}
Use profiles
...
<beans profile="dev">
<bean id="b1" class="MockFSClient" />
</beans>
<beans profile="uat,prod">
<bean id="b1" class="RestFSClient" />
</beans>
...
... -Dspring.profiles.active=dev ...
Probably the best approach is to use profiles as described by Evgeniy Dorofeev in which case you wouldn't need your custom System property. But here's another solution if you were to stick to your custom System property using Conditional Bean Configuration.
In this approach you would need two custom Conditions to encapsulate your conditions:
public class MockFSCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String fsMockProperty = context.getEnvironment().getProperty("com.fs.mock");
return fsMockProperty!=null && fsMockProperty.toLowerCase().equals("true");
}
}
public class RestFSCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String fsMockProperty = context.getEnvironment().getProperty("com.fs.mock");
return fsMockProperty==null || !fsMockProperty.toLowerCase().equals("true");
}
}
In which case your configuration class would look like this:
#Configuration
#ComponentScan(basePackages = {"your.beans.package"})
public class Config {
#Bean
public FSClient mockFSClient(){
return new MockFSClient();
}
#Bean
public FSClient restFSClient(){
return new RestFSClient();
}
#Bean(name="fsManager")
#Conditional(MockFSCondition.class)
public FSManager mockFsManager() {
return new FSManager(mockFSClient());
}
#Bean(name="fsManager")
#Conditional(RestFSCondition.class)
public FSManager restFsManager() {
return new FSManager(restFSClient());
}
}
Notice that we have created two beans with the same name fsManager each one of which is annotated with the two conditions above.
So now at runtime Spring would look for that custom System prop and given whether it's there/correct or not it will instantiate the correct version of FSManager constructor-injected with the right FSClient.
Related
I am implementing a custom Spring context customizer, as I have to perform some operations during startup of the application. The result of the operation is need to configure the datasource I need in my application.
My problem is now, that I need for those operations access to my configuration properties (from application.yaml), as they are the base for my operations.
My, simplified, implementation looks currently like this. Nothing special.
public class MyContextCustomizerFactory
implements ContextCustomizerFactory {
#Target(TYPE) #Retention(RUNTIME)
#Documented #Inherited
public #interface EnabledPostgresTestContainer {
}
#Override
public ContextCustomizer createContextCustomizer(Class<?> c,
List<ContextConfigurationAttributes> a) {
}
static class MyContextCustomizer implements ContextCustomizer {
#Override
public void customizeContext(ConfigurableApplicationContext c,
MergedContextConfiguration mc) {
}
}
}
Of is there an alternatvie approach. Using Springs DynamicPropertySource is currently not an option.
Not sure ContextCustomizerFactory is what you are looking for, because you are talking about "application" but ContextCustomizerFactory is designed for running tests, anyway...
What exactly has confused you?
public class MyContextCustomizerFactory implements ContextCustomizerFactory {
#Override
public ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
return new MyContextCustomizer();
}
}
public class MyContextCustomizer implements ContextCustomizer {
#Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableEnvironment environment = context.getEnvironment();
// reading properties
String applicationName = environment.getProperty("application.name");
// enriching properties
Properties jdbcProperties = new Properties();
jdbcProperties.put("spring.datasource.url", "jdbc://....");
environment.getPropertySources().addFirst(
new PropertiesPropertySource("customizerProperties", jdbcProperties)
);
}
}
UPD.
If the goal is to modify env/properties after Spring has parsed #Configuration classes with #PropertySource we may use BeanFactoryPostProcessor, below are some examples from spring:
EmbeddedDataSourceBeanFactoryPostProcessor - very similar to what TC needs
PropertySourceOrderingPostProcessor - reorders property sources
PropertyOverrideConfigurer
I have a service that uses some object as a generic
#Component
#RequiredArgsConstructor
public class SomeGenericService<T extends Base> {
private final T base;
public void someWork(String info) {
base.someAction(info);
}
}
I also have 3 Base implementations marked with #Component(Base1, Base2, Base3)
I want spring itself to create a service with the generic it needs, for the following example
#Component
#RequiredArgsConstructor
public class Runner implements CommandLineRunner {
private final SomeGenericService<Base1> s1;
private final SomeGenericService<Base2> s2;
private final SomeGenericService<Base3> s3;
#Override
public void run(String... args) throws Exception {
String someString = "text";
s1.someWork(someString);
s2.someWork(someString);
s3.someWork(someString);
}
}
But after the launch, the spring does not understand what I want from it.
Parameter 0 of constructor in SomeGenericService required a single bean, but 3 were found:
- base1: defined in file [Base1.class]
- base2: defined in file [Base2.class]
- base3: defined in file [Base3.class]
Is it possible to set this to automatic, without manually configuring it via the #Bean annotation for each service?
You need to define how those beans should be injected. It's a good practice to have some #Configurations for this purpose. Something like:
#Configuration
#Import({
Base1.class,
Base2.class,
Base3.class
})
public class SomeConfig {
#Bean
SomeGenericService<Base1> someGenericService1() {
return new SomeGenericService(new Base1());
}
#Bean
SomeGenericService<Base2> someGenericService2() {
return new SomeGenericService(new Base2());
}
#Bean
SomeGenericService<Base3> someGenericService3() {
return new SomeGenericService(new Base3());
}
}
I am currently working on a spring-library that allows user-defined config-classes (has nothing to to with #Configuration) to be adjusted from another part of the application before they are used:
interface ConfigAdjuster<T extends Config<T>> {
void adjust(T t);
}
abstract class Config<T extends Config<T>> {
#Autowired
Optional<ConfigAdjuster<T>> adjuster;
#PostConstruct
private void init() {
//i know this cast is somewhat unsafe, just ignore it for this question
adjuster.ifPresent(a -> a.adjust((T)this));
}
}
This can be used as follows:
class MyConfig extends Config<MyConfig> {
//imagine many fields of more complex types
public String myData;
}
#Configuration
class MyConfigDefaults {
#Profile("dev")
#Bean
public MyConfig devDefaults() {
//imagine setting defaults values here
return new MyConfig();
}
}
Now a consumer of the library that uses MyConfig can do the following somewhere in his application:
#Bean
public ConfigAdjuster<MyConfig> adjustDefaults() {
return cfg -> {
cfg.myData = "something_other_than_default";
}
}
The biggest problem I see with this approach is that the whole "adjust the config"-part is somewhat hidden for the user. You can not easily tell you are able to change the default-configuration by using a ConfigAdjuster. In the worst case the user tries to autowire the config object and tries to modify it that way which results in undefined behaviour because other components could already have been initialized with the defaults.
Is there an easy way to make this approach more "telling" than what it is right now? The whole idea is to not copy&paste the whole default-config + adjustment parts across multiple projects.
One way to make all of this more explicit would be to require the adjuster in the constructor of Config, but this pollutes every constructor and usage of the inherting classes.
Any thoughts on this?
Edit: Do note that this is a simplified version of the library and I do know about the implications of a private #PostConstruct etc. If you have another way of achieving all of this without the #PostConstruct please do share :)
Edit2:
Let me outline the main goals of this library again:
Allow the definition of default config-objects for the library-user
Allow the enduser (consuming a depedency using this library) to overwrite certain parts of the default configuration before it is used
Save the library-user from boilerplate (e.g. define 2. on their own)
There is two solution for your problem:
1- define a generic Customizer something like:
public interface Customizer<T> {
T customize(T t);
boolean supports(Class target);
}
in your lib you have a config:
public class MyConfig {
private String property;
public MyConfig() {
}
public void setProperty(String property) {
this.property = property;
}
}
so your Default configuration should look something like this:
#Configuration
public class DefaultConfiguration {
#Autowired(required = false)
private List<Customizer> customizers;
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig();
myConfig.setProperty("default value");
if (customizers != null) {
for (Customizer c : customizers) {
if (c.supports(MyConfig.class)) {
return (MyConfig) c.customize(myConfig);
}
}
}
return myConfig;
}
}
this way, the only thing the user should do whenever he wants to customize you bean is to implement Customizer, and then declare it as a bean.
public class MyConfigCustomizer implements Customizer<MyConfig> {
#Override
public MyConfig customize(MyConfig myConfig) {
//customization here
return myConfig;
}
#Override
public boolean supports(Class<?> target) {
return MyConfig.class.isAssignableFrom(target);
}
}
and he should declare it:
#Bean
public Customizer<MyConfig> customizer(){
return new MyConfigCustomizer ();
}
I think this answers your question, but it's ugly (uncheched warnings and a List ...) not the best, as everything seems to the user customizable even it's not.
2- I suggest you expose interfaces for Beans that can be adjusted by the user, something like:
public interface MyConfigCustomizer{
MyConfig customize(MyConfig config);
}
your Default Configuration:
#Configuration
public class DefaultConfiguration {
#Autowired(required = false)
private MyConfigCustomizer customizer;
#Bean
public MyConfig myConfig() {
MyConfig myConfig = new MyConfig();
myConfig.setProperty("default value");
if (customizer != null) {
return customizer.customize(myconfig);
}
return myConfig;
}
}
this way the user knows that MyConfig can be adjusted (and not all the beans).
I have to create a configurable pointcut. Can anyone help for achieving the dynamic pointcut.
DynamicPointcut.class
public class DynamicPointcut extends DynamicMethodMatcherPointcut {
#Value("${custom.logging.basepackage}")
String basePackage;
#Override
public ClassFilter getClassFilter() {
return new ClassFilter() {
#Override
public boolean matches(Class<?> clazz) {
List<Class<?>> classList = ClassFinder.find(basePackage);
return classList.stream().anyMatch(x -> x.equals(clazz));
}
};
}
#Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
if(args.length>0){
return true;
}
return false;
}
}
ConfigurableAdvisorConfig.class
#Configuration
public class ConfigurableAdvisorConfig {
#Autowired
private ProxyFactoryBean proxyFactoryBean;
#Autowired
DefaultPointcutAdvisor defaultPointcutAdvisor;
DynamicPointcut pointcut = new DynamicPointcut();
NonProductionLoggingAspect advice = new NonProductionLoggingAspect();
String[] advisor;
List<Advisor> advisorList = new ArrayList<Advisor>();
#Bean
public String[] defaultPointcutAdvisor(){
defaultPointcutAdvisor.setAdvice(new NonProductionLoggingAspect());
defaultPointcutAdvisor.setPointcut(new DynamicPointcut());
advisor = new String[]{"defaultPointcutAdvisor"};
return advisor;
}
#Bean
public ProxyFactoryBean proxyFactoryBean(){
proxyFactoryBean.setInterceptorNames(advisor);
return proxyFactoryBean;
}
}
You can wire up your aspect using the static aspectOf factory method (you can't see that method, it is added by the aspectj compiler)
<bean class="com.YourAspect" factory-method="aspectOf">
<property name="basePackage"
value="${custom.logging.basepackage}" />
Reference:
autowiring in aspect
Use AspectJ support in Spring for your scenario.
#Aspect
#Component
public class DaoAspect{
#Pointcut("within(com.xyz..dao.*)")
public void allDao(){};
#Before("allDao")
public void runAdvise(){
//some code
}
}
Define DaoAspect
Enable aspectJ support in spring by using #EnableAspectJAutoProxy
Ensure your aspect gets registered as bean via component scanning
There you go, this way you can advise all the classes in a
particular package
Let's say I have a bean that needs a value to be injected into one of its fields:
#Component
#PropertySource("classpath:spring-files/my-values.properties")
public class MyArbitraryClass implements ArbitraryClass {
#Value("#{'${values.in.property.file}'.split(',')}")
private List<String> values;
private boolean myBoolean;
#PostConstruct
private void determineBoolean() {
for (String value : values)
if (System.getProperty("whatever").contains(value))
myBoolean = true;
}
#Override
public boolean getMyBoolean() {
return myBoolean;
}
}
I want to use the determineBoolean() method as a condition to instantiate another bean. So I implement the Condition interface:
public class MyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getBeanFactory().getBean(ArbitraryClass.class).getMyBoolean();
}
}
I componentScan MyArbitraryClass in order to make sure it instantiates first (as opposed to declaring the bean definition in a JavaConfig file):
#Configuration
#ComponentScan("com.package.that.contains.my.arbitrary.class")
public class Conf {
#Bean
#Conditional(MyCondition.class)
// the bean type doesn't really matter
Reader reader() throws FileNotFoundException {
return new FileReader(new File(""));
}
#Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
The value injected to the 'values' field in MyArbitraryClass will be null, when evaluated by MyCondition. I'm pretty sure that this is because the application context that is injected into the matches() method in MyCondition, contains (at the time) the beans before they were processed by the BeanFactoryPostProcessors (in this case PropertySourcesPlaceHolderConfigurer).
Is there a way to get the bean in the matches() method after being processed, and all values injected into it?
I would suggest is to encapsulate the search-for-existing-property in your custom condition rather than having the match logic on another bean.
With that approach you could get hold of the Environment directly from the exposed context, so your MyCondition would look like this:
public class MyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return Arrays.stream(context.getEnvironment().getProperty("values.in.property.file").split(","))
.anyMatch(propValue -> propValue.equals(System.getProperty("whatever")));
}
}