I just want to implement custom attributes inject in spring boot.But it does
not work well.
First, I create a annotation like below
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ConfigValue {
String name();
}
Second,I create a config class with customize annotation like below
#Configuration
public class MysqlConf {
#Value("${spring.datasource.driverClassName}")
private String className;
#Value("${spring.datasource.url}")
private String jdbcUrl;
#Value("${spring.datasource.username}")
private String username;
#ConfigValue(name = "spring.datasource.password")
private String password;
#Bean
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(className);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setUrl(jdbcUrl);
druidDataSource.setMaxActive(20);
druidDataSource.setInitialSize(1);
druidDataSource.setMinIdle(1);
druidDataSource.setMaxWait(60000);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(300000);
druidDataSource.setValidationQuery("select 1");
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setPoolPreparedStatements(true);
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(50);
return druidDataSource;
}
}
Then I set custom attributes like below
#Component
public class BeanPropertiesUtil implements
InitializingBean,BeanFactoryAware {
private static final Logger logger =
LoggerFactory.getLogger(BeanPropertiesUtil.class);
private BeanFactory beanFactory;
public static Map<String,Object> configMap = new HashMap<>();
#PostConstruct
public void init() throws Exception {
configMap.put("spring.datasource.password","123321");
configMap.put("orderNo","2018051929991");
}
#Override
public void afterPropertiesSet() throws Exception {
logger.info("config map : "+ JSON.toJSONString(configMap));
ApplicationContext applicationContext =
BeanNameUtil.getApplicationContext();
final String[] beanDefinitionNames =
applicationContext.getBeanDefinitionNames();
for(String beanName : beanDefinitionNames) {
Object bean = beanFactory.getBean(beanName);
final Field[] declaredFields = bean.getClass().getDeclaredFields();
if(declaredFields.length > 0) {
for(Field field : declaredFields) {
final ConfigValue configValue =
field.getAnnotation(ConfigValue.class);
if(configValue != null) {
field.setAccessible(true);
logger.info("field name :"+field.getName() +" field
value:"+configMap.get(configValue.name()));
field.set(bean,configMap.get(configValue.name()));
}
}
}
}
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
It cannot get password value, I also create an other class
#Service
public class MyAnnotationService {
#ConfigValue(name = "spring.datasource.password")
private String password;
public void deSth() {
System.err.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:"+password);
}
}
it works,
I do not know why MysqlConf can't get the value but MyAnnotationService can.Please help me,Thanks!
bean.getClass().getDeclaredFields();
not returning fields for #Configuration MysqlConf class, it works if you change MysqlConf to #Service
Related
I am registering beans dynamically at startup with BeanDefinitionRegistryPostProcessor:
Bean class:
#RequiredArgsConstructor
public class DynamicBean {
private final String name;
private final Object value;
}
Properties class:
#Data
public class DynamicProperties {
private List<Bean> beans;
#Data
public static class Bean {
private String name;
private Object value;
}
}
BeanDefinitionRegistryPostProcessor:
#Configuration
public class DynamicBeanRegistrar implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
DynamicProperties dynamicProperties = Binder.get(environment)
.bind("dynamic", Bindable.of(DynamicProperties.class))
.orElseThrow(IllegalStateException::new);
dynamicProperties.getBeans().forEach(bean -> {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicBean.class);
String beanName = bean.getName();
ConstructorArgumentValues cav = new ConstructorArgumentValues();
cav.addGenericArgumentValue(bean.getName());
cav.addGenericArgumentValue(bean.getValue());
beanDefinition.setConstructorArgumentValues(cav);
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
log.info("Registered dynamic bean")
});
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// noop
}
}
I also have another configuration class using #ConditionalOnBean annotation for some beans:
#Configuration(proxyBeanMethods = false)
public class AnotherConfiguration {
#Bean
public Object a() {
return new Object ();
}
#Bean
public Object b() {
return new Object ();
}
#Bean
#ConditionalOnBean(DynamicBean.class)
public Object x() {
return new Object();
}
#Bean
#ConditionalOnBean(DynamicBean.class)
public Object y() {
return new Object();
}
}
The problem is that the #ConditionalOnBean annotation doesn't seem to know about the dynamically registered beans and is therefore evaluating as false, causing beans x and y not to be created.
I have tried using #AutoConfigureBefore(AnotherConfiguration.class) on my DynamicBeanRegistrar as well as implementing PriorityOrdered, but that didn't seem to have any effect.
Is there any way to achieve the expected behaviour?
My Spring Boot application implements the TenantStore example for storing data in ThreadLocalTargetSource detailed in this link
#Bean(destroyMethod = "destroy")
public ThreadLocalTargetSource threadLocalTenantStore() {
ThreadLocalTargetSource result = new ThreadLocalTargetSource();
result.setTargetBeanName("tenantStore");
return result;
}
The working example allows for the TenantStore object to be set and injected by the Spring Framework. My version of the TenantFilter class described in that article sets the properties of the TenantStore object whenever a Servlet request is made
#Autowired
private TenantStore tenantStore;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
String token = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
if (token != null) {
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (oAuth2AccessToken.getAdditionalInformation() != null) {
String tenantName = (String) oAuth2AccessToken.getAdditionalInformation().get("tenant");
storeTenantInThread(tenantName);
}
}
}
chain.doFilter(request, response);
} catch (ResourceNotFoundException e) {
log.error(e.getMessage());
} finally {
clearTenant();
}
}
private void storeTenantInThread(String tenantName) {
tenantStore.setName(tenantName);
}
private void clearTenant() {
tenantStore.clear();
}
I then have a number of services where TenantStore is autowired and in each of these services the TenantStore contains the information that was populated in the doFilter() method. Except for one class. For some reason the properties of the TenantStore in this class are still null. The name of the class affected is MyCacheService and the architecture is as follows:
#RestController
#RequestMapping("/here")
public class MyController {
#Autowired
private MyService myService
#GetMapping
public ResponseEntity myGetMethod(#RequestParam("text") String text) {
myService.myMethod(text);
return new ResponseEntity(Http.OK);
}
}
#Service
public class MyService {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyOtherService myOtherService;
public void myMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
myOtherService.myOtherMethod(text);
}
}
#Service
public class MyOtherService {
#Autowired
private TenantStore tenantStore;
#Autowired
private Map<String, MyComponent> myComponents;
public void myOtherMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
MyComponent useThisComponent = myComponents.get("componentName");
useThisComponent.myComponentMethod(text);
}
}
#Component("componentName")
public class MyComponent {
#Autowired
private TenantStore tenantStore;
#Autowired
private MyCacheService myCacheService;
public void myComponentMethod(String text) {
System.out.println(tenantStore.getName()); //works - prints name
entityAliasCacheService.myCacheMethod(String text);
}
}
#Service
public class MyCacheService {
#Autowired
private TenantStore tenantStore;
public void myCacheMethod(String text) {
System.out.println(tenantStore.getName()); //DOES NOT WORK - tenantStore object is not null but the name property is
}
}
From what I can guess, for some reason the TenantStore in MyCacheService is being populated in a different thread, though I've no idea why.
I noticed similar behaviour. I fixed the issue by adding a bean dependancy
#Service
#DependsOn("proxiedThreadLocalTargetSource") // asks Spring to first load proxy bean
public class MyCacheService {
where proxiedThreadLocalTargetSource bean is defined like in the OP's example -
#Primary
#Bean(name = "proxiedThreadLocalTargetSource")
public ProxyFactoryBean proxiedThreadLocalTargetSource(ThreadLocalTargetSource threadLocalTargetSource) {
ProxyFactoryBean result = new ProxyFactoryBean();
result.setTargetSource(threadLocalTargetSource);
return result;
}
So, by adding the dependancy, Spring knows that it should load MyCacheService bean after the proxiedThreadLocalTargetSource. Without this dependancy, I noticed that TenantStore got injected instead of the proxy bean.
Getting instance of TenantStore from org.springframework.context.ApplicationContext
First implement ApplicationContextAware like as below
#Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext context() {
return context;
}
}
And your MyCacheService Will be like this:
public class MyCacheService {
public void myCacheMethod(String text) {
TenantStore tenantStore = ApplicationContextUtil.context().getBean(TenantStore.class);
System.out.println(tenantStore.getName());
}
}
enter image description hereThis is the code I wrote to start the Resource. Not able to register the Resouce Class. Used Mongo database and the basics of #Injected
#JsonIgnoreProperties(ignoreUnknown = true)
public class Student extends BaseModel {
private String firstName;
private String lastName;
private String marks;
private long dob;
#Email
private String email;
public Student() {
}
public Student(String firstName, String lastName, String marks, long dob, #Email String email) {
this.firstName = firstName;
this.lastName = lastName;
this.marks = marks;
this.dob = dob;
this.email = email;
}
//Getter Setter method
}
//Base Repository Interface
public interface BaseRepository<T extends BaseModel> extends GenericRepository<T> {
T save (T model);
List<T> getAll();
T getById(String Id);
void deleteById(String Id);
T updateById(String Id, T model);
}
Abstract Repository Method to perform the Basics method of CRUD Operation
public BaseRepositoryImpl(MongoDb mongoManager, Class<T> clazz) throws Exception{
this.mongoManager = mongoManager;
collectionName = clazz.getAnnotation(CollectionName.class);
collection = mongoManager.getMongoCollection(collectionName.name());
entityClass = clazz;
}
#Override
public T save(T model) {
if(model == null){
throw new RuntimeException("No data Found");
}
Object id = collection.save(model).getUpsertedId();
if(id != null && id instanceof ObjectId){
model.setId(((ObjectId) id).toStringMongod());
}
return model;
}
}
//service
public interface StudentService {
Student save(Student student);
}
//Resource
WebService
#Path("/student")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#HK2Managed
#Timed
public class StudentResource {
#Inject
StudentService studentService;
/* #Inject
public StudentResource(StudentService studentService) {
this.studentService = studentService;
}*/
#Path("/getName")
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getName(){
return "Puru";
}
}
Configuration of the Application to Register the Uri and port of the Main Class
private String template;
// private StudentService studentService;
private String defaultName = "Stranger";
public MongoConfiguration getMongoConfiguration() {
return mongoConfiguration;
}
public void setMongoConfiguration(MongoConfiguration mongoConfiguration) {
this.mongoConfiguration = mongoConfiguration;
}
#Valid
#JsonProperty("mongoserver")
public MongoConfiguration mongoConfiguration;
}
//Application Connector to bind the Class in interface of the Respected Class
public class ApplicationConnector extends AbstractModule /*AbstractBinder*/ {
private final ConfigurationContext context;
public ApplicationConnector(ConfigurationContext context) {
this.context = context;
}
#PostConstruct
#Override
protected void configure() {
bind(StudentService.class).to(StudentServiceImpl.class);
bind(StudentRepository.class).to(StudentRepositoryImpl.class);
}
// applicaytion Connector Class
public class App extends Application<AppConfiguration> {
GuiceBundle<AppConfiguration> guiceBundle = null;
private JAXWSBundle jaxwsBundle = new JAXWSBundle();
public static void main(final String[] args) throws Exception {
new App().run(args);
}
private JAXWSBundle<Object> jaxWsBundle = new JAXWSBundle<>("/api");
#Override
public String getName() {
return "student-DropWizard-demo";
}
#Override
public void initialize(final Bootstrap<AppConfiguration> bootstrap) {
// TODO: application initializatio
// bootstrap.getObjectMapper().enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
// bootstrap.addBundle(GuiceBundle.<AppConfiguration>builder()
// .enableAutoConfig("package.to.scan")
// .searchCommands(true)
// .build()
// );
/* GuiceBundle.Builder builder = GuiceBundle.builder() .noDefaultInstallers()
.enableAutoConfig("com.dzone");
guiceBundle = builder.build();*/
// bootstrap.addBundle(GuiceBundle.builder().enableAutoConfig("com.dzone")
// .build());
// Module[] modules = autoDiscoverModules();
bootstrap.getObjectMapper().registerSubtypes(DefaultServerFactory.class);
//bootstrap.getObjectMapper().registerModule(new FCSerializerModule());
bootstrap.getObjectMapper().enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
bootstrap.addBundle(jaxwsBundle);
GuiceBundle.Builder builder = GuiceBundle.builder()
// .modules(modules)
.noDefaultInstallers()
.installers(new Class[]{LifeCycleInstaller.class,
ManagedInstaller.class,
JerseyFeatureInstaller.class, ResourceInstaller.class,
JerseyProviderInstaller.class,
EagerSingletonInstaller.class,
HealthCheckInstaller.class,
TaskInstaller.class,
PluginInstaller.class
})
.enableAutoConfig(ApplicationConnector.class.getPackage().getName());
postInitialize(bootstrap, builder);
guiceBundle = builder.build();
bootstrap.addBundle(guiceBundle);
}
#Override
public void run(final AppConfiguration configuration,
final Environment environment) throws Exception {
// TODO: implement application
// FilterRegistration.Dynamic dFilter = environment.servlets().addFilter("student", CrossOriginFilter.class);
// AbstractServerFactory sf = (AbstractServerFactory) configuration.getServerFactory();
// Endpoint e = jaxWsBundle.publishEndpoint(
// new EndpointBuilder("student", new StudentResource()));
// environment.jersey().register(new StudentResource());
//environment.jersey().register(new StudentServiceImpl());
//environment.jersey().packages("service");
// environment.jersey().disable();
// environment.servlets().addServlet(StudentResource.class).addMapping("/student");
environment.getJerseyServletContainer().getServletInfo();
//environment.servlets().setBaseResource("");
///environment.servlets().addServlet("StudentResource",StudentResource.class);
//environment.jersey().register(new ResourceInstaller());
postRun(configuration,environment);
}
protected void postRun(final AppConfiguration configuration, final Environment environment) throws Exception {
// Sub-classes should
}
protected void postInitialize(Bootstrap<AppConfiguration> bootstrapm, GuiceBundle.Builder guiceBuilder) {
// Sub-classes should
}
/*public Module[] autoDiscoverModules() {
Reflections reflections =
new Reflections(
new ConfigurationBuilder()
.forPackages(
"com.dzone"));
Set<Class<? extends AbstractModule>> classes = reflections.getSubTypesOf(AbstractModule.class);
List<Module> discoveredModules = new ArrayList<>();
for (Class clazz : classes) {
try {
AbstractModule module = (AbstractModule) clazz.newInstance();
discoveredModules.add(module);
} catch (Exception e) {
e.printStackTrace();
}
}
return discoveredModules.toArray(new Module[]{});
}
}
This is the output of the Code. It's not able to register the the resource of the application
WARN [2019-09-09 05:32:31,707] io.dropwizard.setup.AdminEnvironment:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! THIS APPLICATION HAS NO HEALTHCHECKS. THIS MEANS YOU WILL NEVER KNOW !
! IF IT DIES IN PRODUCTION, WHICH MEANS YOU WILL NEVER KNOW IF YOU'RE !
! LETTING YOUR USERS DOWN. YOU SHOULD ADD A HEALTHCHECK FOR EACH OF YOUR !
! APPLICATION'S DEPENDENCIES WHICH FULLY (BUT LIGHTLY) TESTS IT. !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
INFO [2019-09-09 05:32:31,711] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler#615db358{/,null,AVAILABLE}
INFO [2019-09-09 05:32:31,718] org.eclipse.jetty.server.AbstractConnector: Started application#f9cab00{HTTP/1.1,[http/1.1]}{0.0.0.0:8099}
INFO [2019-09-09 05:32:31,719] org.eclipse.jetty.server.AbstractConnector: Started admin#10272bbb{HTTP/1.1,[http/1.1]}{0.0.0.0:8091}
INFO [2019-09-09 05:32:31,719] org.eclipse.jetty.server.Server: Started #5573ms
INFO [2019-09-09 05:32:31,719] com.roskart.dropwizard.jaxws.JAXWSEnvironment: No JAX-WS service endpoints were registered.
In the example above you declare Resource as HK managed:
#HK2Managed
#Timed
public class StudentResource {
Which means that HK2 (jersey's DI) will instantiate object and not guice.
But your service is declared in guice module:
public class ApplicationConnector extends AbstractModule /*AbstractBinder*/ {
private final ConfigurationContext context;
public ApplicationConnector(ConfigurationContext context) {
this.context = context;
}
#PostConstruct
#Override
protected void configure() {
bind(StudentService.class).to(StudentServiceImpl.class);
bind(StudentRepository.class).to(StudentRepositoryImpl.class);
}
So it's normal that your service can't inject StudentService as HK2 is not aware of guice dependencies.
This could be fixed by activating HK2-guice bridge so HK could use guice bindings:
Add dependency: org.glassfish.hk2:guice-bridge:2.5.0-b32 (version must match HK2 version, used by dropwizard)
Enable option: .option(GuiceyOptions.UseHkBridge, true)
In your github project (student-gradle) you have an opposite situation:
Resource is guice managed (guice creates resource instance)
#Path("/student")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Timed
public class StudentResource {
And service is declared in HK:
public class ApplicationConnector extends /*AbstractModule*/ AbstractBinder {
#PostConstruct
#Override
protected void configure() {
bind(StudentService.class).to(StudentServiceImpl.class);
bind(StudentRepository.class).to(StudentRepositoryImpl.class);
}
}
(but this module is actually never registered)
My suggestion is to "live" in guice context: don't use #HKManaged (it was added only for edge cases) and use guice modules for services declaration. This way guice will be responsible for all DI related to your code.
I created an annotation for creating ThreadPoolTaskExecutors populated with values from the environment. However, when I autowire the bean it gives me a proxy and calling the methods on the proxy gives the wrong values.
If I manually access the target class, then I get the correct values.
Executor exec = (Executor) ((Advised) executor).getTargetSource().getTarget();
ThreadPoolTaskExecutor taskExec = (ThreadPoolTaskExecutor) exec;
I have been scratching my head for a while now as to why I'm getting a proxy bean, but can't seem to figure it out.
I am using an annotation to import my registrar class that implements ImportBeanDefinitionRegistrar to register the bean. The registrar code is below:
public class ExecutorEnumerationRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
public static final String CORE_POOL_SIZE = "corePoolSize";
public static final String MAX_POOL_SIZE = "maxPoolSize";
public static final String QUEUE_CAPACITY = "queueCapacity";
public static final String THREAD_NAME_PREFIX = "threadNamePrefix";
private static final String REJECTED_EXECUTION_HANDLER = "rejectedExecutionHandler";
private static final String NAMES = "names";
private static final String REJECTED_HANDLER = "rejectedHandler";
private Environment env;
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(ThreadPoolTaskExecutorCreator.class.getName(), true);
final String[] beanNames = (String[]) attrs.get(NAMES);
final String[] policyClass = (String[]) attrs.get(REJECTED_HANDLER);
for (int x = 0; x < beanNames.length; x++) {
createAndRegisterBean(beanNames[x], policyClass[x], registry);
}
}
private void createAndRegisterBean(String name, String policyClass, BeanDefinitionRegistry registry) {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(ThreadPoolTaskExecutor.class);
bd.setAutowireCandidate(true);
bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
MutablePropertyValues mpv = bd.getPropertyValues();
populateProperties(mpv, name, policyClass);
registry.registerBeanDefinition(name, bd);
}
private void populateProperties(MutablePropertyValues mpv, String name, String policyClass) {
mpv.add(CORE_POOL_SIZE, Integer.valueOf(env.getProperty(name + "." + CORE_POOL_SIZE)));
mpv.add(MAX_POOL_SIZE, Integer.valueOf(env.getProperty(name + "." + MAX_POOL_SIZE)));
mpv.add(QUEUE_CAPACITY, Integer.valueOf(env.getProperty(name + "." + QUEUE_CAPACITY)));
try {
mpv.add(REJECTED_EXECUTION_HANDLER, Class.forName(policyClass).newInstance());
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
mpv.add(THREAD_NAME_PREFIX, name + "-");
}
#Override
public void setEnvironment(Environment environment) {
env = environment;
}
}
Annotation to import the registrar:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
#Import(ExecutorEnumerationRegistrar.class)
public #interface ThreadPoolTaskExecutorCreator{
String[] names();
String[] rejectedHandler() default ThreadPoolPolicyHandlers.CALLER_RUNS_POLICY;
}
I have tested with the following code:
Spring Boot Class:
#EnableDiscoveryClient
#ComponentScan("my.test.classes")
#ThreadPoolTaskExecutorCreator(names = {"testExecutor"}, rejectedHandler = ThreadPoolPolicyHandlers.DISCARD_POLICY)
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
SessionAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
JndiDataSourceAutoConfiguration.class,
JndiConnectionFactoryAutoConfiguration.class,
RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
public class TestBoot {
public static void main(String[] args) {
SpringApplication.run(TestBoot.class, args);
}
}
All versions from spring-boot-starter-parent 1.4.5.RELEASE
I wrote a JUnit test that checks the values and it passes. The only time it doesn't work is when I autowire it in a Spring Boot eureka application. Is there anything I can do so that it doesn't autowire a proxy bean? I have searched through the documentation and looked at all the related classes, but I don't see anything related to why it's a proxy. Also, why does it give incorrect values when accessed through the proxy?
Seems you are missing the code for registering the instance of your ImportBeanDefinitionRegistrar ( in your example that is ExecutorEnumerationRegistrar )
So there are two ways to register the ImportBeanDefinitionRegistrar use the #Import annotation directly or implement the ImportSelector interface which can give you more generic configuration options.
For your case simply adding the #Import({ExecutorEnumerationRegistrar.class}) on the Configuration class will do the trick.
#EnableDiscoveryClient
#ComponentScan("my.test.classes")
#ThreadPoolTaskExecutorCreator(names = {"testExecutor"}, rejectedHandler = ThreadPoolPolicyHandlers.DISCARD_POLICY)
// THIS IS REQUIRED
#Import({ExecutorEnumerationRegistrar.class})
// See Above
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
SessionAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
JndiDataSourceAutoConfiguration.class,
JndiConnectionFactoryAutoConfiguration.class,
RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
public class TestBoot {
public static void main(String[] args) {
SpringApplication.run(TestBoot.class, args);
}
}
and just remember to use the #Qualifier when autowiring the instance of ThreadPoolTaskExecutor. See example component
#Component
public class Component {
#Autowired
#Qualifier("testExecutor")
private ThreadPoolTaskExecutor exec;
// you methods
}
So I have an Akka SyncWriteJournal (still in progress) and while unit testing the persistence I always get this error:
java.lang.IllegalArgumentException: no matching constructor found on class com.example.CustomJournal for arguments []
I have one constructor for CustomJournal which takes a few arguments necessary for the persistence to work. If I add an empty constructor to solve the error message the test throws and NPE because it needs fields that should be initiated in the constructor.
Why does it call the empty constructor (I never do it) by itself? And how can I solve this problem?
Code:
Class which creates actors:
public class AkkaActors {
private final ActorSystem actorSystem;
private final ActorRef journal;
private final ActorRef akkaPersistence;
public AkkaActors(JdbcTemplate jdbcTemplate, ObjectMapper objectMapper) {
this.actorSystem = ActorSystem.create("akkaSystem");
this.akkaPersistence = actorSystem.actorOf(Props.create(AkkaPersistence.class), "akkaPersistence");
this.journal = actorSystem.actorOf(Props.create(CustomJournal.class, jdbcTemplate, objectMapper, actorSystem), "customJournal");
}
Akka persistence actor:
public class AkkaPersistence extends UntypedPersistentActor {
private final static String PERSISTENCE_ID = "persistent_actor";
public AkkaPersistence() {
}
#Override
public void onReceiveRecover(Object msg) throws Exception {
if (msg instanceof String) {
System.out.println("msg");
}
}
#Override
public void onReceiveCommand(Object msg) throws Exception {
if (msg instanceof String) {
final String message = (String) msg;
persist(message, new Procedure<String>() {
#Override
public void apply(String message ) throws Exception {
getContext().system().eventStream().publish(message );
}
});
} else {
unhandled(msg);
}
}
#Override
public String persistenceId() {
return PERSISTENCE_ID;
}
}
Custom journal class (without contents of implemented methods):
public class CustomJournal extends SyncWriteJournal {
JdbcTemplate jdbcTemplate;
ObjectMapper objectMapper;
ActorSystem actorSystem;
public CustomJournal(JdbcTemplate jdbcTemplate, ObjectMapper objectMapper, ActorSystem actorSystem) {
this.jdbcTemplate = jdbcTemplate;
this.objectMapper = objectMapper;
this.actorSystem = actorSystem;
}
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
public class AkkaActorsTest{
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
private ObjectMapper objectMapper;
#Test
public void test() throws OperationNotSupportedException {
AkkaActors actors = new AkkaActors(jdbcTemplate, objectMapper);
actors.persist("test");
}