#SpringBootApplication
public class SfgDiApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(SfgDiApplication.class, args);
PetController petController = ctx.getBean("petController", PetController.class);
System.out.println("--- The Best Pet is ---");
System.out.println(petController.whichPetIsTheBest());
}
#Controller
#ResponseBody
public class PetController {
public PetController(PetService petService) {
this.petService = petService;
}
private final PetService petService;
#GetMapping("pet-type")
public String whichPetIsTheBest(){
return petService.getPetType();
}
}
public interface PetService {
String getPetType();
}
#Service("cat")
public class CatPetService implements PetService {
#Override
public String getPetType() {
return "Cats Are the Best!";
}
}
#Profile({"dog", "default"})
public class DogPetService implements PetService {
public String getPetType(){
return "Dogs are the best!";
}
}
application.properties
spring.profiles.active=dog
Result
--- The Best Pet is ---
Cats Are the Best!
I don't understand why cats are here. I can even comment the properties out, but cats are still here. I would like to see dogs.
What can I try next?
It looks like the DogService is not a bean. So in the end you only have a single bean (CatService) and this one will be picked all the time.
You should define a bean of DogPetService with #service.
You should add the cat profile to CatPetService.
Like this :
#Service("cat")
#Profile({"cat"})
public class CatPetService implements PetService {
#Override
public String getPetType() {
return "Cats Are the Best!";
}
}
#Profile({"dog", "default"})
#Service("dog")
public class DogPetService implements PetService {
public String getPetType(){
return "Dogs are the best!";
}
}
I think you can achieve what you want like this:
#Configuration("petConfig")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class PetConfig {
#Value("${spring.profiles.active}")
private String type;
#Bean(name = "petService")
#Primary
public PetService petService() {
PetService petService = null;
if ("cat".equals(type)) {
petService = new CatPetService();
}
if ("dog".equals(type)) {
petService = new DogPetService();
}
return petService;
}
}
Annotate both CatPetService and DogPetService with #Service. This way you can easily adapt the code without hardcodings and duplication.
Related
I'm creating pojo class and store the application.properties variable but I'm getting null values
NOTE: need to access env from my Abstract class
POJO class
package mynt.xyz.c4.pushnotif.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#Configuration("notificationEnvironment")
#ConfigurationProperties(prefix = "app.notif")
public class NotificationEnvironment {
private String key;
private String url;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Initializing class with #autowired
public abstract class NotificationBase {
#Autowired
NotificationEnvironment notificationEnvironment;
public void getEnv(){
system.out.println(notificationEnvironment.getKey()); // null value
}
}
concrete class that extend to my NotificationBaseClass
#Component
#Qualifier("androidNotification")
public class AndroidNotification extends NotificationBase implements Notification {
public AndroidNotification(String message, String title, String datalink, List<String> instanceIds) {
super(message, title, datalink, instanceIds);
}
AndroidNotification(){
super();
}
#Override
public void send() {
this.getEnv();
}
}
application.properties
app.notif.key=jkashdkjashd
app.notif.url=https/some.url
You can auto wire #Configuration class from #Configuration class
#Configuration class may reference the instance of any other #Configuration class using #Autowired. This works because the #Configuration classes themselves are instantiated and managed as individual Spring beans.
Make your class #Component and add prefix value in #ConfigurationProperties, like this. This works for me, hope this works for you as well.
#Component
#ConfigurationProperties(prefix = "app.notif")
public class NotificationEnvironment {
private String key;
private String url;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
You can use this properties like this:
#Component
public class NotificationBase {
private static NotificationEnvironment notificationEnvironment;
#Autowired
public NotificationBase(NotificationEnvironment notificationEnvironment){
this.notificationEnvironment = notificationEnvironment;
}
public static void getEnv(){
System.out.println(notificationEnvironment.getKey()); // null value
}
}
Here is the one of the concrete class definition as OP author mentioned.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ConcreteNotification extends NotificationBase {
#Autowired
public ConcreteNotification(NotificationEnvironment notificationEnvironment) {
super(notificationEnvironment);
}
}
updated NotificationBase as below
public abstract class NotificationBase {
NotificationEnvironment notificationEnvironment;
public NotificationBase(NotificationEnvironment notificationEnvironment) {
this.notificationEnvironment = notificationEnvironment;
}
public void getEnv(){
System.out.println(notificationEnvironment.getKey());
}
}
The controller class I am using to get configuration values
#RestController
public class ArticleCommentController {
#Autowired
ConcreteNotification concreteNotification;
#RequestMapping(value = "/health_check", method = RequestMethod.GET)
public void getDemo() {
concreteNotification.getEnv();
}
}
output:
jkashdkjashd
I am playing around with the Spring framework and I would like to get my name returned from the cache. After 5 seconds I will update the cache and I hope to receive a new name.... unfortunately this is not working.... why?
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
public void run(String... args) throws Exception {
Test test = new Test();
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}
#SpringBootApplication
#EnableCaching
#EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You are creating an instance of Test yourself with new, you are not autowiring it. I would try like this:
#Component
public class Test {
public String name = "peter";
#Cacheable(value = "numCache")
public String getName() {
return name;
}
#Scheduled(fixedRate = 5000)
#CachePut(value = "numCache")
public String setName() {
this.name = "piet";
return name;
}
}
#Component
public class AppRunner implements CommandLineRunner {
#Autowired private Test test;
public void run(String... args) throws Exception {
while(true) {
Thread.sleep(1000);
System.out.println(test.getName());
}
}
}
How can I choose a service implementation depending on a request parameter on SpringBoot? I can do this by manually instantiating the service, but that's not making use of the Spring Injection feature.
I can also Autowire the two services separately but I'm thinking that if I have more implementations that would pollute the class.
Here's a crude example:
#RestController
class RestControllerTest {
#Autowired
PizzaService pizzaService;
public void bakePizza(#RequestParam("type") String type,#RequestParam("extra") String extra) {
if (type.equals("cheese")) {
//set pizzaService Cheese Implementation
pizzaService = new CheezePizza();
} else {
//set PizzaService vegetable Impleentation;
pizzaService = new VegetablePizza();
}
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
public abstract class PizzaService {
String ingredients;
public abstract void prepareIngredients(String exraIngredient);
public void bakePizza() {
System.out.println("baking pizza with " + ingredients);
}
}
class CheezePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Cheese " + exraIngredient;
}
}
class VegetablePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Vegetable " + exraIngredient;
}
}
You could autowire list of beans of same type. So let's say you add getType() to your PizzaService and register every type as spring bean.
public abstract class PizzaService {
abstract String getType();
}
#Component
class CheezePizza extends PizzaService {
#Override
public String getType() {
return "cheese";
}
}
#Component
class VegetablePizza extends PizzaService {
#Override
public String getType() {
return "vegetable";
}
}
#RestController
class RestControllerTest {
private final Map<String, PizzaService> pizzaServices;
public RestControllerTest(List<PizzaService> services) {
pizzaServices = services.stream().collect(Collectors.toMap(PizzaService::getType, Function.identity()));
}
public void bakePizza(#RequestParam("type") String type, #RequestParam("extra") String extra) {
PizzaService pizzaService = pizzaServices.get(type); // remember of handling missing type
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
Another way is to use name your beans by convention i.e. cheesePizza, vegetablePizza and then use ApplicationContext#getBean(type + "Pizza") but I like first approach better, because it's less magical.
Take the following general abstract class:
#Configurable
public abstract class TestEntityRoot {
public abstract String print();
}
And a subclass:
#Configurable
public class TestEntity extends TestEntityRoot{
private TestEntityService testEntityService;
#Autowired
public void setTestEntityService(TestEntityService testEntityService) {
this.testEntityService = testEntityService;
}
#Override
public String print() {
return testEntityService.print();
}
}
When call controller:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntity entity = new TestEntity();
return entity.print();
}
}
everything ok. But if call like this:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntityRoot entity = new TestEntity();
return entity.print();
}
}
i get null pointer. Is it possible that second example work?
In the second case you create manually the class rather than using spring's bean. Autowire the bean instead. See
#RestController
public class TestEntityController {
#Autowired
private TestEntity entity
#GetMapping(name = "/test")
public String print() {
return entity.print();
}
}
I've got an entity
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#Column(name = "id", unique = true, nullable = false)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I initialize it like this
#PostConstruct
public void init() {
List<String> newFiles = this.listFiles();
newFiles.forEach(filename -> {
Book book = new Book();
book.setName(filename);
dbRepository.save(book);
});
}
If I set the result of save to an instance of Book, I can get the id and it is not null—so id is created fine.
I defined a repository
#RepositoryRestResource
public interface IBooksRepository extends CrudRepository<Book, Long> {
}
which I'd like to use to get and set data into the books table in the database.
When I try to access my repository rest using curl localhost:8080/books, I get this response
{
"_embedded":{
"books":[
{
"name":"simple-file.txt",
"_links":{
"self":{
"href":"http://localhost:8080/books/1"
},
"book":{
"href":"http://localhost:8080/books/1"
}
}
}
]
},
"_links":{
"self":{
"href":"http://localhost:8080/books"
},
"profile":{
"href":"http://localhost:8080/profile/books"
}
}
}
The books element returns name only. How can I make it return id too, on the same level as name?
Spring Data Rest hides the ID by default, in order to have it in the JSON you have to manually configure that for your entity. Depending on your spring version you can either provide your own configuration (old):
#Configuration
public class ExposeEntityIdRestConfiguration extends RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
}
}
...or register a RepositoryRestConfigurer (current):
#Component
public class ExposeEntityIdRestMvcConfiguration extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
}
}
See the Spring Data Rest documentation for more details.
The accepted answer overrides a deprecated method. Here's the updated version:
#Component
public class RestConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.exposeIdsFor(Book.class);
}
}
An alternative approach is to implement RepositoryRestConfigurer in your #SpringBootApplication annotated class:
#SpringBootApplication
public class MyApplication implements RepositoryRestConfigurer {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config, CorsRegistry cors) {
config.exposeIdsFor(Book.class);
}
}
There is now a static method RepositoryRestConfigurer.withConfig that does the same thing as above. See javadoc:
Convenience method to easily create simple {#link RepositoryRestConfigurer} instances that solely want to tweak the {#link RepositoryRestConfiguration}.
I found the usage in one of their integration tests
So the following approach would be more up to date as of now:
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer()
{
return RepositoryRestConfigurer.withConfig(config -> {
config.exposeIdsFor(Book.class);
});
}
#Component
public class RestConfig implements RepositoryRestConfigurer {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(Book.class);
//config.exposeIdsFor(Library.class);
}
}
This is a solution which works for all entities
#Autowired
private EntityManager entityManager;
#Bean
public RepositoryRestConfigurer repositoryRestConfigurer() {
return RepositoryRestConfigurer.withConfig(config -> config.exposeIdsFor(entityManager.getMetamodel().getEntities().stream().map(Type::getJavaType).toArray(Class[]::new)));
}
This is a good way to go.
#Projection(name = "customBook", types = { Book.class })
public interface CustomBook {
#Value("#{target.id}")
long getId();
}
credit: https://www.baeldung.com/spring-data-rest-projections-excerpts