Spring Autowired object have all null field when meet #Cachable - spring

#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
}
#Service
class UserServiceBuilder(){
public UserService build(ConfigBean config){
UserService s = new UserServcie()
s.name = config.xxxx
//other config omitted
return s;
}
}
#Configuration
class AppConfig{
#Bean
public UserService UserService(UserServiceBuilder builder, ConfigBean configBean) {
return builder.load(configBean);
}
}
class UserCtrl {
#Autowired
private UserService UserService; // get null when the #Cachable
}
UserService is created by the UserServiceBuilder which will read a log of properties from the config file.
Then the UserService will be injected to UserCtrl, it works at first.
However once I add the #Cachable to one method of UserService, all the fileds of the injected UserService will be null.
It seems like that spring will create a proxy of UserService when using cache, and the proxy object does not have the fileds.
How to fix that?

Yes, you are right it's because of the proxy. You must add getters in the UserService and use this getters if you want take the fields of the UserService outside.
#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
//ADD THIS:
public String getName() {
return this.name;
}
}
But if you add output inside UserService method:
public User getUser(String name) {
System.out.println("PING " + this.name);
...
}
you will see that this.name inside object is not null.
P.S. and I think that you can remove #Service annotation from UserService. Because you have #Service and #Bean registration of UserService. It's confusing.

Related

can i controll the #Valid, #Transactional order in the web tier

#RestController
public class GoodsController {
#Autowired
private GoodsDao goodsDao;
#Autowired
private GoodsService goodsService;
#PostMapping("test1")
#Transactional
public String test1(#RequestBody #Valid GoodsSaveParam goodsSaveParam) {
goodsDao.selectOne(new QueryWrapper<Goods>().eq("code", goodsSaveParam.getGoodsCode()));
return "test1";
}
#PostMapping("test2")
#Transactional
public String test2(#RequestBody GoodsSaveParam goodsSaveParam) {
goodsService.updateById(goodsSaveParam);
return "test2";
}
}
#Data
public class GoodsSaveParam {
#GC
private String goodsCode;
private String goodsName;
}
#Component
public class GCValidator implements ConstraintValidator<GC, String> {
#Autowired
private GoodsDao goodsDao;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
goodsDao.selectOne(new QueryWrapper<Goods>().eq("code", value));
return true;
}
}
#Service
#Validated
public class GoodsService {
#Autowired
private GoodsDao goodsDao;
public void updateById(#Valid GoodsSaveParam goodsSaveParam) {
goodsDao.selectOne(new QueryWrapper<Goods>().eq("code", goodsSaveParam.getGoodsCode()));
}
}
I have a GoodsController and write 2 test method(test1 and test2) implement the same logic(each logic query the same thing twice) except the annotation location, i mean the #Transational and #Valid,in the method test1, the validator and test1's login is not hit the cache. in the test2, i wrap the query login into a class and put #Valid into its'method signature, so the second can hit the session cache. the test2 is obvious call that the validator must be in the transanction. So if there have any method for user to implement same effect in form.

Does spring java config support field injection? where field is private and has no setter method

How I may inject a private '#Autowire/#Inject' field in a bean while wiring by java config. If you see the example below:
public class HowToGetField2ByJavaConfig {
private Field1 field1;
#Inject
private Field2 field2;
#Inject
public HowToGetField2ByJavaConfig(Field1 field1) {
this.field1 = field1;
}
}
By using an AppConfig
#Configuration
static class AppConfig {
/....
/....
/....
#Inject
public HowToGetField2ByJavaConfig howToGetField2ByJavaConfig(Field1 field1) {
HowToGetField2ByJavaConfig howToGetField2ByJavaConfig = new HowToGetField2ByJavaConfig(field1);
//here how to inject Field2
return howToGetField2ByJavaConfig;
}
...
I wouldn't suggest doing this, but it can be done.
Spring #Autowired tries to inject beans by Name and by Type.
So if you want to create the Beans how you asked, you could do this:
#Configuration
#ComponentScan("nl.testing")
public class AppConfig {
#Bean
public Field1 field1() {
// This will be injected inside your bean method below which creates the TooLongName bean
return new Field1();
}
#Bean
public Field2 field2() {
// Via the `#Autowired` this will be injected in the Field of your
// TooLongName class (this has preference because it matches the name)
return new Field2();
}
#Bean
public Field2 otherField2() {
// This won't be used because `field2()` is prefered.
return new Field2();
}
#Bean
public TooLongName tooLongName(Field1 field1) {
TooLongName tooLongName = new TooLongName(field1);
return tooLongName;
}
}

how to use session scoped bean in jhipster microservice architecture

My session scoped bean:
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class AuthNodes {
private String authNodes;
public String getAuthNodes() {
return authNodes;
}
public void setAuthNodes(String authNodes) {
this.authNodes = authNodes;
}
}
is injected in a REST controller of a JHipster generated microservice:
#RestController
#RequestMapping("/api")
public class NodeResource {
#Autowired
private AuthNodes authNodes;
...
#GetMapping("/nodes-and-children/{user:.+}")
#Timed
public ResponseEntity<List<Node>> getFilteredNodesAndChildren(#PathVariable String user,
#ApiParam Pageable pageable) {
...
String hosts = authNodes.getAuthNodes();
if (hosts == null) {
authNodes.setAuthNodes("my user's authorized node names");
}
...
but at each call the previously set value is lost and authNodes.getAuthNodes() returns null.
What is wrong?
Thanks, Mic

#CachePut does not work in #Configuration for pre cache

I was trying to use spring stater-cache in spring boot 1.3.5, everything works fine except pre load cache in #Configuration class.
Failed tests:
CacheTest.testCacheFromConfig: expected:<n[eal]> but was:<n[ot cached]>
Please take a look at the code as below, if you met this before, please share it with me :)
#Component
public class CacheObject{
#CachePut(value = "nameCache", key = "#userId")
public String setName(long userId, String name) {
return name;
}
#Cacheable(value = "nameCache", key = "#userId")
public String getName(long userId) {
return "not cached";
}
}
#Component
public class CacheReference {
#Autowired
private CacheObject cacheObject;
public String getNameOut(long userId){
return cacheObject.getName(userId);
}
}
#Configuration
public class SystemConfig {
#Autowired
private CacheObject cacheObject;
#PostConstruct
public void init(){
System.out.println("------------------");
System.out.println("-- PRE LOAD CACHE BUT DIDN'T GET CACHED");
System.out.println("------------------");
cacheObject.setName(2, "neal");
cacheObject.setName(3, "dora");
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BootElastic.class)
#WebAppConfiguration
public class CacheTest {
#Autowired
private CacheObject cacheObject;
#Autowired
private CacheReference cacheReference;
#Test
public void testCache(){
String name = "this is neal for cache test";
long userId = 1;
cacheObject.setName(userId, name);
// cacheObject.setName(2, "neal"); // this will make test success
String nameFromCache = cacheReference.getNameOut(userId);
System.out.println("1" + nameFromCache);
Assert.assertEquals(nameFromCache, name);
}
#Test
public void testCacheFromConfig(){
String nameFromCache = cacheReference.getNameOut(2);
System.out.println("4" + nameFromCache);
Assert.assertEquals(nameFromCache, "neal");
}
}
#PostConstruct methods are called right after all postProcessBeforeInitialization() BeanPostProcessor methods invoked, and right before postProcessAfterInitialization() invoked. So it is called before there is any proxy around bean, including one, putting values to cache.
The same reason why you can't use #Transactional or #Async methods in #PostConstruct.
You may call it from some #EventListener on ContextRefreshedEvent to get it working

#Value returning null

I have a couple #Value annotations within my Entity Class. I am not sure why but they are both returning null. I also have "ShaPasswordEncoder" Object Autowired, which too is throwing a NullPointer Exception. I have no idea why. Please advise.
#Repository
#Configurable
#Entity
#Table(name="user")
#NamedQueries({...})
public class User implements Serializable{
#Transient private static final Logger logger = Logger.getLogger(User.class);
#Transient private static final AppUtil appUtil = new AppUtil();
#Transient #Value("some value") private String extDir;
#Transient #Value("100x100") private String imageSize;
#Transient private static byte[] salt = "FBar".getBytes();
#Transient #Autowired private ShaPasswordEncoder passwordEncoder;
....
//Default Constructor
public User(){
logger.info("TEST IMAGE => "+imageSize);
}
public String passwordEncoder(String password) {
return passwordEncoder.encodePassword(password,salt);
}
Making a JPA entity as a Spring bean is a bad design.
You should keep your entity simple: only getters and setters.
// Only JPA annotations
#Entity
#Table(name="user")
#NamedQueries({...})
public class User {
// Getters & Setters
}
Then you should delegate the business logic to service classes:
#Service
public class UserService {
#Autowired
private ShaPasswordEncoder passwordEncoder;
#Value("${conf.extDir}")
private String dir;
// Some operations ...
public void createUser(User user) {
// ...
}
public void updateUser(User user) {
// ...
}
}
Are you passing valid value expressions? For properties placeholder you can use something like:
#Value("${directory.extDirectory}")
You can also use Spring EL and get all the goodness from it using the #{value} check the docs here
Is also possible to assign a default value in case the property is not found
#Value("${directory.extDirectory:defaultValue}")
Using Spring annotations on a POJO means you are delegating the creation and the configuration of this bean to the Spring IoC Container !!!
The Spring container will supply all the required dependencies.
For example:
#Component
public class MyBean {
#Value("${data}")
private String data;
#Autowired
private MyService service;
// ...
}
When you try to instantiate a bean using the new operator, you will get null values.
MyBean bean = new MyBean();
In order to have a fully-configured bean you MUST get it from the applicationContext. The Spring container will provide all the requested dependencies.
MyBean bean = (MyBean) applicationContext.getBean(MyBean.class);
Or
#Component
public class AnotherBean {
// You are sure that Spring will create and inject the bean for you.
#Autowired
private MyBean bean;
}
Although the bean is managed by Spring it is also possible that you make a new bean yourself instead of getting it from spring container.
So below code will make a new user but it is not get from Spring context.
User user = new User()
If you use above code the #value is not applied to your bean.
If you want you must get the User from Spring by #Autowired
public class SampleService{
#Autowired
private User user;
public void Sample(){
user.getExtDir(); //here user.extDir is not null
}
}

Resources