Aspect Advice for Spring Data Repository doesnt work - java

im trying to create some pointcuts and before advices for Repositories in order to enable filtering over entitymanager for some Repositories in Spring Data in Spring Boot. i also have web and service layer in project and AspectLogging works for both. But i couldnt do same for repositories. i have been struggling for 2 days and i tried so many things for fix it. i read almost every docs, issues and threads about this( proxy issues CGlib and JDK Proxy etc). i used jhipster for creating project.
i cant deploy Application except #Pointcut with CrudRepository. and even its deployed #Before isnt called for a method call in Repository. i think i have a similar Problem like in following question. proxy confusion
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xxx.zzz.business.repository.ApplyRepository com.xxx.zzz.web.rest.applyResource.ApplyRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applyRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy173]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy173
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 61 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'applyRepository': Post-processing of FactoryBean's singleton object failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy173]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy173
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1523)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:314)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1120)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1044)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 63 more
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy173]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy173
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:212)
at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:109)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:447)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:333)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:293)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1719)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:113)
... 70 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy173
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:56)
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:202)
... 77 more
Does anyone know what its could be ?
Classes and Configs look like following.
Repository:
public interface ApplyRepository extends JpaRepository<Apply,Long>,QueryDslPredicateExecutor<Apply> {
public Page<Apply> findAll(Predicate predicate, Pageable p);
}
...
}
Database Config:
#Configuration
#EnableJpaRepositories("com.xxx.zzz.business.repository")
#EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
#EnableTransactionManagement//(proxyTargetClass = false)
public class DatabaseConfiguration {
....
AspectJ Config:
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
// #EnableLoadTimeWeaving(aspectjWeaving = ... )
public class LoggingAspectConfiguration {
...
System Architecture:
#Aspect
public class SystemArchitecture {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.someapp.web package or any sub-package
* under that.W
*/
#Pointcut("within(com.xxx.zzz.web.rest..*)")
public void inWebLayer() {
}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.someapp.service package or any sub-package
* under that.
*/
#Pointcut("within(com.xxx.zzz.business.service..*)")
public void inServiceLayer() {
}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.someapp.dao package or any sub-package
* under that.
*/
#Pointcut("within(com.xxx.zzz.business.repository..*)")
public void inDataAccessLayer() {
}
/**
* All layers
*/
#Pointcut("inWebLayer() || inServiceLayer() || inDataAccessLayer()")
public void inALL(){
}
#Pointcut("within(org.springframework.data.repository.CrudRepository)")
//#Pointcut("execution(*org.springframework.data.repository.Repository+.* (..))")
//#Pointcut("execution(* com.xxx.zzz.business.repository+.*(..))")
//#Pointcut("execution(* org.springframework.data.jpa.repository.JpaRepository+.*(..))")
//#Pointcut("execution(* com.xxx.zzz.business.repository.ApplyRepository.*(..))")
public void inDATAExec(){}
}
FilterAspect:
#Aspect
#Transactional
public class FilterAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#PersistenceContext
private EntityManager entitymanager;
#Before("com.xxx.zzz.aop.logging.SystemArchitecture.inDATAExec())") // "execution(* com.xxx.zzz.business.repository.InvoiceRepository.*(..))"
public void doAccessCheck() {
if (TransactionSynchronizationManager.isActualTransactionActive() && SecurityUtils.isAuthenticated()) {
Session session = entitymanager.unwrap(Session.class);
session.enableFilter("GLOBAL_FILTER").setParameter("customerId", SecurityUtils.getCurrentCustomerId());
}
}
EDIT: i solved problem. it was related somehow to wrong pointscuts and names. i tried to change pointcuts for custom annotation in Repository. it doesnt work for method or class level. i read in following links issues about this.
advice 1
advice 2
im struggling for hours for target and annotation. but no result.
is it really imposible to add custom annotations in Spring Data Repositories for advicing ?
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
//#Inherited
public #interface CustomerRequired {
String value() default "customerrequired";
}
Repository:
public interface InvoiceRepository extends JpaRepository<Invoice,String>, QueryDslPredicateExecutor<Invoice> {
#CustomerRequired
public Page<Invoice> findAll(Predicate predicate, Pageable p);
...
}
Pointcut and Advice:
#Pointcut(value = "#target(customerRequired)",argNames = "customerRequired")
public void targetCustomer(#SuppressWarnings("unused") CustomerRequired customerRequired) {/**/}
#Before(value = "com.xxx.zzz.aop.logging.SystemArchitecture.targetCustomer(customerRequired) && com.xxx.zzz.aop.logging.SystemArchitecture.inDataLayer()")
public void doAccessCheck(JoinPoint joinPoint, CustomerRequired customerRequired) {
if (TransactionSynchronizationManager.isActualTransactionActive() && SecurityUtils.isAuthenticated()) {
Session session = entitymanager.unwrap(Session.class);
session.enableFilter("GLOBAL_FILTER").setParameter("customerId", SecurityUtils.getCurrentCustomerId());
}
}

Instead of using
#Pointcut("within(org.springframework.data.repository.CrudRepository)")
public void inDATAExec(){}
use like following
#Pointcut("this(org.springframework.data.repository.Repository)")
public void inDATAExec(){}
and what it does is
any join point (method execution only in Spring AOP) where the
proxy implements the Repository interface
You can have a look it at http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
Hope it helps!

Related

coundn't get bean when running a jar packed from a springboot project

I can run my springboot project in IDEA nicely but when packed it to a jar and run with the java command, just got the java.lang.NullPointerException when getting a bean from spring context.
the first class which just got errors:
#Service
public class MdspiImpl extends CThostFtdcMdSpi {
public MdspiImpl(CThostFtdcMdApi mdapi) {
m_mdapi = mdapi;
logger.info("MdspiImpl is creating...");
***mdr = SpringContextUtil.getBean("marketDataRobot");//this is the error code***
}
}
the second class:
#Service
public class MarketDataRobot {
}
the SpringContextUtil class:
#Component("SpringContextUtil")
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
the gradle file:
jar {
baseName = 'programmingTrading'
version = '0.1.0'
manifest {
attributes 'Main-Class': 'com.blackHole.programmingTrading'
}
}
the running exception:
WARN main[AbstractApplicationContext.java:557 Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mdspiImpl' defined in URL [jar:file:/E:/workspace/simuPrd/programmingTrading-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/com/blackHole/programmingTrading/infrastructure/MdspiImpl.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.blackHole.programmingTrading.infrastructure.MdspiImpl]: Constructor threw exception; nested exception is java.lang.NullPointerException]
[com.blackHole.programmingTrading.infrastructure.MdspiImpl]: Constructor threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:184)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:300)
... 27 common frames omitted
Caused by: java.lang.NullPointerException: null
at com.blackHole.programmingTrading.SpringContextUtil.getBean(SpringContextUtil.java:35)
at com.blackHole.programmingTrading.infrastructure.MdspiImpl.<init>(MdspiImpl.java:46)
It also stem from another problem: #Autowired annotation doesn't work...
when using like this:
#Component
public class Scu{
}
in another class:
#Autowired
private Scu scu;
logger.info(String.format("MdspiImpl is creating...[%s]", scu.toString()));
will get a java.lang.NullPointerException: null
spring-boot configuration like this:
#SpringBootApplication
public class ProgrammingTrading {
public static void main(String[] args) {
SpringApplication.run(ProgrammingTrading.class, args);
}
}
that is part of reasons of using SpringContextUtil to get the bean...
thanks a lot!
SpringContextUtil shouldn't be a accessed statically like you are doing... Since you define it as a #Component do the following;
#Service
public class MdspiImpl extends CThostFtdcMdSpi {
#Autowired
private SpringContextUtil springContextUtil;
public MdspiImpl(CThostFtdcMdApi mdapi) {
m_mdapi = mdapi;
logger.info("MdspiImpl is creating...");
***mdr = springContextUtil.getBean("marketDataRobot");
}
}
Due to SpringContextUtil not being injected via Spring, but simply accessed statically, the applicationContext inside of it is ignored and is null in your case.
Also remove the static modifier;
#Component
public class SpringContextUtil implements ApplicationContextAware {
private ApplicationContext applicationContext;
// include getter/setter for applicationContext as well
public <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
edit
The trouble from the latest example project;
#Service
public class ExampleService {
#Autowired
private Logger logger;
public ExampleService() {
this.logger=logger;
logger.info("Im working");
}
}
Here the Logger will be null, when the ExampleService constructor is triggered, since the constructor is called before the injection starts, but you can merge this behaviour if you incorporate the injection through the said constructor as follows;
#Service
public class ExampleService {
private final Logger logger;
public ExampleService(Logger logger) {
this.logger = logger;
logger.info("Im working");
}
}
Works perfectly without any trouble...
You should never be accessing beans programmatically like you did with this SpringContextUtil, just inject MarketDataRobot in the constructor of MdspiImpl and you’re good to go (since it’s annotated with #Service). The preferred way is to use constructor injection instead of field injection, which will make it easier for you to write unit tests. You can also get rid of #Autowired if you have only one constructor.

How the directory structure for SpringBoot MongoDB CrudRepositoryCustom Implementation should be organized?

I have created a simple maven project using SpringBoot and MongoDB. I have two repository implementations i.e. StudentRepository and StudentRepositoryCustom. StudentRepository extends in built MongoRepository and the custom repository. The custom repository methods are implemented in StudentRepositoryImpl. The applications runs without errors when I put StudentRepository, StudentRepositoryCustom and StudentRepositoryImpl in same package i.e. com.aman.springboot.repository. But the application throws error when the implementation class is moved to some other package let's say com.aman.springboot.impl.
What am I doing wrong ?
Here's the main class:
package com.aman.springboot.client;
#SpringBootApplication(scanBasePackages = "com.aman.springboot")
public class ApplicationLauncher {
public static void main(String[] args) {
SpringApplication.run(StudentController.class, args);
}
}
Here's RestController class:
package com.aman.springboot.controller;
#RestController
#EnableAutoConfiguration
#EnableMongoRepositories(basePackages = "com.aman.springboot.repository")
#RequestMapping(value = "/student")
public class StudentController {
#Autowired
private StudentRepository studentRepository;
#RequestMapping(value = "/getStudent", method = RequestMethod.GET)
public StudentRepo getStudent(#RequestParam(required = true) int id) {
return studentRepository.findStudentById(id);
}
#RequestMapping(value = "/removeStudent", method = RequestMethod.POST)
public void removeStudent(#RequestBody(required = true) StudentRepo
studentRepo) {
studentRepository.deleteStudent(studentRepo);
}
}
Here's StudentRepository:
package com.aman.springboot.repository;
#Repository
public interface StudentRepository extends MongoRepository<StudentRepo,
String>, StudentRepositoryCustom {
public StudentRepo findStudentById(int id);
}
Here's StudentRepositoryCustom:
package com.aman.springboot.repository;
public interface StudentRepositoryCustom {
public void deleteStudent(StudentRepo studentRepo);
}
Here's StudentRepositoryImpl:
package com.aman.springboot.impl;
#Service
public class StudentRepositoryImpl implements StudentRepositoryCustom{
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private StudentRepo student;
#Override
public void deleteStudent(StudentRepo studentRepo) {
mongoTemplate.remove(studentRepo);
}
}
As you can see both interfaces or repositories are in same package but the implementation class for StudentRepositoryCustom interface is in different package. In this case the application throws error while deploying:
Here's the stack trace:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error
creating bean with name 'studentController': Unsatisfied dependency
expressed through field 'studentRepository'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'studentRepository': Invocation of init method failed; nested
exception is org.springframework.data.mapping.PropertyReferenceException: No
property deleteStudent found for type StudentRepo! at
org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject
(AutowiredAnnotationBeanPostProcessor.java:586)
~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE] at
org.springframework.beans.factory.annotation.InjectionMetadata.inject
(InjectionMetadata.java:91) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at
org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues
(AutowiredAnnotationBeanPostProcessor.java:372) ~[spring-beans-
5.0.8.RELEASE.jar:5.0.8.RELEASE] at
.
.
.
.
org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE] at
com.aman.springboot.client.ApplicationLauncher.main
(ApplicationLauncher.java:17) [classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'studentRepository': Invocation of init method
failed; nested exception is
org.springframework.data.mapping.PropertyReferenceException: No property
deleteStudent found for type StudentRepo! at
org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.initializeBean
(AbstractAutowireCapableBeanFactory.java:1699) ~[spring-beans-
5.0.8.RELEASE.jar:5.0.8.RELEASE] at
.
.
.
.
tializeBean(AbstractAutowireCapableBeanFactory.java:1695) ~[spring-beans-
5.0.8.RELEASE.jar:5.0.8.RELEASE] ... 29 common frames omitted
The application works fine if I move the StudentRepositoryImpl class to package in which the repositories are i.e. com.aman.springboot.repository.
Any help would be appreciated !!! Thanks.
I solved my problem by refactoring the package structure for custom repository and its implementation class. What I understand from the problem is Spring looks for implemented classes for repositories in child packages.
For example, if repository is defined in com.foo.repo, it's implementing class should be in com.foo.repo.impl.
The repositories should be defined in top level packages and the implementing class should be in same package or it's child package.

Use entityManager in a single repository

I use spring-data-jpa to access my data. I need a way to detach an Object and store it as a new database row. My approach is currently to add a detach method to the repository, but for that, I need a EntityManager. And I haven't found a (nice) way of obtaining it... Any ideas?
#Repository
public interface InteractionRepository
extends JpaRepository<Interaction, Long>,
DetatchableItemRepository{}
public interface DetatchableItemRepository {
void detach(Interaction clone);
}
public class DetatchableItemRepositoryImpl implements DetatchableItemRepository {
#Autowired
EntityManager em;
public void detach(Interaction clone) {
em.detach(clone);
clone.id=null;
em.persist(clone);
}
}
However, spring dies with this error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'interactionRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property detach found for type Interaction!
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property detach found for type Interaction!
you use wrong name convention for custom repository , try this :
public interface DetatchableItemRepositoryCustom {
void detach(Interaction clone);
}
public interface DetatchableItemRepository extends JpaRepository<Interaction, Long>,
DetatchableItemRepositoryCustom {
}
public class DetatchableItemRepositoryImpl implements DetatchableItemRepositoryCustom {
}
spring data use name convention for custom repository and main repository.(see about name Adding custom behavior to single repositories)
If you have some SomeRepository , that extends some base spring data repository , and want to add custom behavior then it should be like :
interface SomeRepositoryCustom{
someMethod();
}
//XXXRepository - any base spring data repository
interface SomeRepository extends<T ,ID> extend XXXRepository , SomeRepositoryCustom {
.......
}
public class ARepositoryImpl implement SomeRepositoryCustom{
#Overide
someMethod(){
....
}

Error creating bean because it's an interface?

Basically I have two beans implementing the same interface. One is for profile "default" and another "integration".
public interface SomeClientIfc { ... }
#Component
#Profile(value={"functional", "integration"})
public class StubSomeNIOClient implements SomeClientIfc {...}
public class SomeNIOClient implements SomeClientIfc {...}
#Configuration
#Profile("default")
public class SomeClientConfiguration {
#Bean
public SomeClientIfc someClient() {
...
SomeNIOClient someClient = new SomeNIOClient(numberOfParititions, controllerHosts, maxBufferReadSize,
connectionPoolSize);
return someClient;
}
}
In prod code it's
#Autowired
public SomeUserResolver(..., SomeClientIfc someClient) {...}
So far so good and I did see the stub bean is called in an integration test. Then I want to inject some test data into the stub bean in my integration test:
#ContextConfiguration(locations = {"/configProperties.xml", "/integrationTests.xml", ...})
#ActiveProfiles("integration")
public class SomeTestBase {
#Autowired
private SomeClientIfc someClientIfc;
}
However, when running the test I got error message
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someClientIfc': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.audiencescience.some.client.SomeClientIfc]: Specified class is an interface
I even tried to replace SomeClientIfc with StubSomeNIOClient but still get the same message, even though StubSomeNIOClient is not an interface.
You should add a Qualifier annotation along with the Autowired one to specify which bean must be instantiated:
#Autowired
#Qualifier("my-bean")
The reason it's trying to inject the SomeClientIfc is because you called the variable 'someClientIfc'.
In the integration environment you have all 3 classes initialized: SomeClientIfc, StubSomeNIOClient, and SomeNIOClient. This creates a confusion for spring, luckily there are ways to resolve that confusion.
One way is as mentioned above by Little Santi, another way is to name your variable 'stubSomeNIOClient' see code below
#ContextConfiguration(locations = {"/configProperties.xml", "/integrationTests.xml", ...})
#ActiveProfiles("integration")
public class SomeTestBase {
#Autowired
private SomeClientIfc stubSomeNIOClient;
}

Adding inner bean to Application Context throws No default constructor found execption

I'm trying to programatically add a inner bean to my application context within a jUnit test. I do not want to pollute my context by having the bean annotated with #Component as it will affect all other tests that run within the same context.
public class PatchBaseImplTest extends TestBase{
/**
* Sample test patch to modify the schema
*/
public class SchemaUpdatePatch extends PatchBaseImpl {
public SchemaUpdatePatch(){
super();
}
#Override
public void applyPatch() throws Exception {
}
};
#Before
public void setUp(){
// add patch to context
beanRegistry.registerBeanDefinition("SchemaUpdatePatch", SchemaUpdatePatch.class, BeanDefinition.SCOPE_PROTOTYPE);
schemaPatch = (Patch)applicationContext.getBean("SchemaUpdatePatch", SchemaUpdatePatch.class);
}
}
where registerBeanDefinition is defined as:
public void registerBeanDefinition( String name, Class clazz, String scope){
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setBeanClass(clazz);
definition.setScope(scope);
definition.setAutowireCandidate(true);
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
registry.registerBeanDefinition(name, definition);
}
I can see that the bean defn has been added to the application context, but when I try to retrieve the bean using appContext.getBean() Spring throws errors that the class is missing a constructor:
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ia.system.patch.PatchBaseImplTest$SchemaUpdatePatch]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.ia.system.patch.PatchBaseImplTest$SchemaUpdatePatch.<init>()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:83)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000)
... 35 more
Caused by: java.lang.NoSuchMethodException: com.ia.system.patch.PatchBaseImplTest$SchemaUpdatePatch.<init>()
at java.lang.Class.getConstructor0(Class.java:2800)
at java.lang.Class.getDeclaredConstructor(Class.java:2043)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78)
... 36 more
I've tried adding a default constructor to the SchemaUpdatePatch class, but it does not seem to matter.
If however, I annotate it with #Component instead of adding it to the context programatically, and try to access it via applicationContext.getBean(), it works fine.
What is the correct way of adding this bean to the applicationContext programatically? Is my GenericBeanDefinition wrong? Am I missing something to specify what the constructor is?
Writing up this post was actually cathartic. Helped me find my bug/error. Have to make the inner class Static or Spring cannot instantiate it. Hopefully this may help someone else in the future.
ie:
/**
* Sample test patch to modify the schema
*/
static public class SchemaUpdatePatch extends PatchBaseImpl {
public SchemaUpdatePatch(){
super();
}
#Override
public void applyPatch() throws Exception {
}
};

Categories