I'm using Spring AOP for exception handling but there is one point that I guess my component class is out of Spring Proxy so Spring AOP annotation that I created doesn't work in that class.
#Configuration
#AllArgsConstructor
public class MGRuleConfig {
private final GRepository repository;
private final GInitializer initializer;
private final GMapper mapper;
#Bean
#Qualifier("mRules")
public List<GRules> mRules(){
SSRule rule1 = new SSRule();
CSRule rule2 = new CSRule();
MPRule rule3 = new MPRule();
EGRule rule4 = new EGRule();
return List.of(rule1, rule2, rule3, rule4);
}
#Bean
public GService gService() {
return new MGServiceImpl(repository, initializer, mapper);
}
}
Then I have this service;
#Service
#RequiredArgsConstructor
public class MGServiceImpl implements GService {
............
#Override
public GaDTO executeRules(String gId, Integer pN) {
Ga ga = repository.findById(gId);
GaDTO gaDTO = mapper.toDTO(ga);
List<GaRules> mRules = (List<GaRules>) applicationContext.getBean("mRules");
mRules.forEach(rule -> rule.apply(gaDTO, pN));
repository.save(mapper.toEntity(gaDTO));
return gaDTO;
}
I need to put my exception handling annotation into that apply method but aspect doesn't work in that method.
#Component
public class SSRule implements GaRules {
#Override
#IPException
public void apply(GaDTO gaDTO, Integer pN) {
PDTO p1 = gaDTO.getP1();
PDTO p2 = gaDTO.getP2();
if (PTEnum.P_1.equals(gaDTO.getPT())) {
sS(gaDTO, pN, p1, p2);
} else {
sS(gaDTO, pN, p2, p1);
}
}
Annotation doesn't work in there. Here's my aspect class;
#Aspect
#Component
public class IPExceptionAspect {
#Around("execution(public * c.m.s.r.i.SSRule.apply(..)) && " +
"#annotation(c.m.s.i.a.IPException)")
public Object checkIP(ProceedingJoinPoint pjp) throws Throwable {
pjp.proceed();
return pjp;
}
}
So, what should I do to make IPException annotation and my Spring AOP work and why doesn't it work?
The problem is your code, you are creating instances of those rules yourself inside a bean method and expose them as a List. Which means the bean is of type List not your own SSRule and thus it won't work.
Instead make an #Bean method, or use the detected instance to inject into the list. As your SSRule is annotated you will already have an instance, just inject that into your #Bean method.
Bean
#Qualifier("mRules")
public List<GRules> mRules(SSRule rule1){
CSRule rule2 = new CSRule();
MPRule rule3 = new MPRule();
EGRule rule4 = new EGRule();
return List.of(rule1, rule2, rule3, rule4);
}
Now you will get the Spring managed instance which will have AOP applied.
Although I would hardly call this AOP as it is too specific for one class (not really crosscutting in that regard).
Related
First, let me introduce a few simple classes and interface to be able to describe my problem.
interface Basic { void foo(); }
interface Extended extends Basic { void bar(); }
class BasicService {
#Inject
List<Basic> basics;
void execute() {
basics.forEach(Basic::foo);
}
}
class ExtendedService {
#Inject
List<Extended> extendeds;
void execute() {
extendeds.forEach(Extended::bar);
}
}
#Configuration
class MyConfiguration {
// Assume, that Basic1 and Basic2 are implementations of Basic and
// Extended1 is an implementation of Extended
#Bean
public Basic basic1() {
return new Basic1();
}
#Bean
public Basic basic2() {
return new Basic2();
}
#Bean
public List<Extended> extendeds() {
return Arrays.asList(new Extended1("0"), new Extended1("1"), new Extended1("2"));
}
#Bean
public BasicService basicService() {
return new BasicService();
}
#Bean
public ExtendedService extendedService() {
return new ExtendedService();
}
}
I have two services that act on different level of abstraction. My problem is that I'm failing to find a way how I can inject all beans that implement the Basic interface in my BasicService. With the current implementation it only injects all Extended implementations, because I have a factory bean method that has a return type of List in its method signature.
I can not change the bean configuration in a way that all Extended beans have their own factory methods, because in my real code the number of Extended implementations is dynamically computed on runtime...
Is there a way I can configure Spring, so that it merges all beans with Basic and List<Basic> together in 1 big list that I can use in my BasicService?
I'm not sure whether there is a way to solve this implicitly, since Spring tries to find the best match, which the List bean seems to be (have you tried generics, e.g., List<? extends Basic> ?).
But you could do it programmatically, by getting the bean definitions from the ApplicationContext:
public BasicService(#Inject ApplicationContext ctx) {
Map<String, ? extends Basic> basicMap = ctx.getBeansOfType(Basic.class);
Map<String, Collection<? extends Basic>> basicCollectionMap = ctx.getBeansofType(ResolvableType.forClassWithGenerics(Collection.class, Basic.class));
// Now merge
Collection<? extends Basic> basics = basicMap.values();
basicCollectionMap.values().stream().map(l -> basics.addAll(l));
}
I just typed this here, so I hope there aren't any obvious errors.
I solved the problem by adding generic bean definitions for the Extended implementations. This can be done by adding a BeanDefinitionRegistryPostProcessor implementation to the #Configuration class as follows:
#Configuration
class MyConfiguration {
#Bean
public static ExtendedBeanFactory extendedBeanFactory() {
return new ExtendedBeanFactory();
}
}
class ExtendedBeanFactory implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
for (int i = 0; i < 3; i++) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Extended1.class);
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue(Integer.toString(i));
beanDefinition.setConstructorArgumentValues(args);
registry.registerBeanDefinition("extended_" + i, beanDefinition);
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// noop
}
}
This has the advantage, that consumer classes can simply inject a list of Basic or a list of Extended as the code from the question does.
I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}
I have a spring boot base abstract config class that creates a bean. If I then inherit from it, the bean will be created later than my controller (which needs to auto-wire it and thus fails). Note: it does get created, just after the controller. So it can't be auto-wired but has to be found via appContext.getBean( BeanType.class )
If I instead override the bean method in the child class, then it's created before the controller and it can be auto-wired.
How can i fix this and make the super-class bean definition load at the same time as the child class?
#SpringBootApplication
public class ChildConfig extends ParentConfig<PCTestBean>
{
public ChildConfig()
{
super();
}
#Override
public PCTestBean getT()
{
return new PCTestBean();
}
}
public abstract class ParentConfig<T>
{
public ParentConfig() {}
#Bean
public T createTestBean()
{
return getT();
}
public abstract T getT();
}
public class PCTestBean
{
}
#RestController
#RequestMapping( "/client" )
public class MyController
{
#Autowired
private PCTestBean pcTestBean;
#RequestMapping( "/get" )
#ResponseBody
public String getClient(HttpServletRequest request) throws Exception
{
return pcTestBean.toString();
}
}
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
#ContextConfiguration(
classes = {
ChildConfig.class
}
)
public class TestConfigs
{
#LocalServerPort
private String port;
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setUp() throws Exception
{
mockMvc = MockMvcBuilders
.webAppContextSetup( context )
.build();
}
#Test
public void testValidCall() throws Exception
{
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get( new URI( "http://localhost:" + port + "/client/get" ) )
)
.andExpect( MockMvcResultMatchers.status().isOk() ).andReturn();
System.out.println( result.getResponse().getContentAsString() );
}
}
When Spring scans your configuration class, ChildConfig, it discovers this inherited method
#Bean
public T createTestBean() {
return getT();
}
and registers a bean definition for it. That bean definition contains metadata about the type of the bean. That type is inferred from the return type of the method. In this case, it's resolved to Object because the type variable T has no bounds in its declaration and because Spring doesn't try to resolve it based on the type argument provided in ChildConfig's extends ParentConfig<PCTestBean> clause.
When Spring then tries to process the
#Autowired
private PCTestBean pcTestBean;
injection target, it looks for a PCTestBean bean, which it doesn't think it has, because the metadata is lacking. IF the bean hasn't been initialized through some other forced order, then Spring has no other information to go on and thinks the bean doesn't exist.
When you change your code to
instead override the bean method in the child class
the return type of the method is PCTestBean which Spring can then match to the #Autowired injection requirement, find (and initialize) the bean, and inject it.
By the time you use ApplicationContext#getBean(Class), the PCTestBean has been initialized. Spring can therefore rely on the actual type of the instance. It'll more or less loop through all beans and check whether beanClass.isInstance(eachBean), returning the one that matches (or failing if more than one does).
Pankaj, in their answer, suggests using #DependsOn (it was wrong when they suggested it, before you edited your question). That can help establish the order I mentioned earlier.
I don't how extensive your configuration class is that you think you need generics to abstract some behavior away, but I would suggest just dropping the generic behavior and be explicit in each class.
Try DependsOn annotation, it guarantees that the child bean should be created after the parent bean
#Configuration
public class ChildConfig extends ParentConfig
{
public ChildConfig()
{
super();
}
#DependsOn("parentConfig")
#Override
public TestBean createTestBean()
{
return super.createTestBean();
}*/
}
public abstract class ParentConfig
{
public ParentConfig() {}
#Bean (name ="parentConfig")
public TestBean createTestBean()
{
return new TestBean();
}
}
I need to create multiple instances of a spring bean (let's call it MainPrototypeBean), which I can do with the prototype scope. It depends on some other beans, and I want to create new instances of them each time the main bean is created. However, there is a shared dependency between some of the beans, let's call it SharedPrototypeBean. How do I inject the same instance of SharedPrototypeBean in each of the dependent beans, while also creating a new instance for each MainPrototypeBean?
I'm looking into implementing a custom scope, but I'm hoping to find a cleaner way. Making any of the beans singletons is not an option, as they need to be isolated between different instances of MainPrototypeBean.
Here's an example of what I'm trying to do:
#SpringBootApplication
public class DIDemo {
public static void main(String[]args){
ConfigurableApplicationContext context = SpringApplication.run(DIDemo.class, args);
context.getBean(MainPrototypeBean.class);
}
#Component #Scope("prototype") static class SharedPrototypeBean {}
#Component #Scope("prototype") static class FirstPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class SecondPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class MainPrototypeBean {
#Autowired FirstPrototypeBean first;
#Autowired SecondPrototypeBean second;
}
}
And the output of executing it is:
FirstPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#1b84f475
SecondPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#539d019
You can use the FactoryBean for complex construction logic. Implement its abstract subclass AbstractFactoryBean for creating a MainPrototypeBean, and inject all three dependent beans into it. You can then wire them together in the createInstance method.
The FactoryBean implementation:
public class MainFactoryBean extends AbstractFactoryBean<MainPrototypeBean> implements FactoryBean<MainPrototypeBean> {
private FirstPrototypeBean firstPrototype;
private SecondPrototypeBean secondPrototpye;
private SharedPrototypeBean sharedPrototype;
public MainFactoryBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
this.firstPrototype = firstPrototype;
this.secondPrototpye = secondPrototype;
this.sharedPrototype = sharedPrototype;
}
#Override
protected MainPrototypeBean createInstance() throws Exception {
MainPrototypeBean mainPrototype = new MainPrototypeBean();
firstPrototype.setSharedPrototypeBean(sharedPrototype);
secondPrototpye.setSharedPrototypeBean(sharedPrototype);
mainPrototype.first = firstPrototype;
mainPrototype.second = secondPrototpye;
//call post construct methods on first and second prototype beans manually
firstPrototype.init();
secondPrototpye.init();
return mainPrototype;
}
#Override
public Class<?> getObjectType() {
return MainPrototypeBean.class;
}
}
Note: sharedPrototype is injected after the post-construct phase in the lifecycle of the first and second prototype. So, if you have post-construction logic in these beans that require the sharedPrototype, you need to manually call the init-method when creating the MainPrototypeBean.
Your annotation - configuration changes as as a consequence. The sharedPrototype attributes are no longer autowired (they are set inside FactoryBean), and MainPrototypeBean is not annotated anymore. Instead you need to create the MainFactoryBean.
#Configuration
public class JavaConfig {
//method name is the name refers to MainPrototypeBean, not to the factory
#Bean
#Scope("prototype")
public MainFactoryBean mainPrototypeBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
return new MainFactoryBean(firstPrototype, secondPrototype, sharedPrototype);
}
//Annotations are not needed anymore
static class MainPrototypeBean {
FirstPrototypeBean first;
SecondPrototypeBean second;
}
#Component
#Scope("prototype")
static class SharedPrototypeBean {
}
#Component
#Scope("prototype")
static class FirstPrototypeBean {
private SharedPrototypeBean shared;
//no autowiring required
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {//reference to shared will be null in post construction phase
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component
#Scope("prototype")
static class SecondPrototypeBean {
private SharedPrototypeBean shared;
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
}
After reading the comments and the other answer, I realized that the design is indeed too complex. I made SharedPrototypeBean, FirstPrototypeBean and SecondPrototypeBean regular POJOs, not managed by Spring. I then create all of the objects in a #Bean annotated method.
#Bean
public MainPrototypeBean mainPrototypeBean() {
Shared shared = new Shared();
First first = new First(shared);
Second second = new Second(shared);
return new MainPrototypeBean(first, second);
}
Having an issue with Spring Autowiring. I have an Integration Test class declared as follows:
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public abstract class BaseIntegrationTest
extends AbstractTestNGSpringContextTests {
#Autowired
protected TestProperties properties;
//... more stuff here
}
The Context configuration looks like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.ourcompany.myapp.it"},
excludeFilters = #ComponentScan.Filter(value = com.inin.wfm.it.config.ConfigPackageExcludeFilter.class, type = FilterType.CUSTOM))
public class TestConfig {
private TestProperties testProperties;
private PropertyService propertyService;
//This both creates and registers the bean with Spring
#Bean
public TestProperties getTestProperties() throws IOException {
if (testProperties == null) {
testProperties = new TestProperties(propertyService());
}
return testProperties;
}
#Bean
public PropertyService propertyService() throws IOException {
if (propertyService == null) {
AppAdminConfig config = new AppAdminConfig.Builder(PropertyService.getEnvironment(), TestConfigKey.ApplicationId)
.checkPropertyHasValue(GlobalConfigKey.KafkaBrokerList.key())
.checkPropertyHasValue(GlobalConfigKey.ZookeeperList.key())
.build();
propertyService = new PropertyService(config.getPropertiesConfig());
propertyService.initialize();
}
return propertyService;
}
}
And this is the bean I'm having trouble with:
#Configurable
public class TestProperties {
private PropertyService propertyService;
public TestProperties(PropertyService propertyService) {
this.propertyService = propertyService;
}
public String getCacheUri(){
return propertyService.getPropertyRegistry().getString(TestConfigKey.CacheUri.key(), Default.CACHE_URI);
}
}
I have multiple Test implementation classes that extend BaseIntegrationTest. All of them but one have valid references to their TestProperties field, but exactly one of the test implementation classes is getting a Null Pointer and throwing an NPE when an attempt is made to reference this.
So the question is, why is Spring #Autowire working fine for 3 different classes that extend the same base class, but wiring up null for the fourth? There is no additional configuration logic in any of the implementations.
For MCVE completeness, here's the bare bones of my impl class
public class CacheIT
extends BaseIntegrationTest {
#Test
public void testUserCache() throws InterruptedException, ExecutionException, TimeoutException {
String uri = properties.getCacheUri() //TODO - NPE here
}
}
I know there's not much to go on... I've been working with Spring for a long time and haven't seen it do this kind of thing before. According to everything I can see it should be working.