How to call a Getter from a Class with Vaadins #Route Annotation - spring-boot

I want to access a getter from a Class that uses Vaadins #Route annotation.
Instantiating the class does not work and autowiring neither
This is my entry point. From here Id like to call the getter from the button in the next class to add the actionlistener(clicklistener) to further utilize its data in a small swing window
#SpringBootApplication
public class App
{
#Autowired
Main main;
public static void main( String[] args )
{
SpringApplicationBuilder builder = new SpringApplicationBuilder(App.class);
builder.headless(false);
ConfigurableApplicationContext configurableApplicationContext = builder.run(args);
View view = new View();
}
}
This is the class which implements Vaadin. Here is the getter I want to call
#Route("index")
public class Main extends VerticalLayout {
VerticalLayout buttons = new VerticalLayout();
TextField field = new TextField();
Button button = new Button("commit change");
public Main() {
button.addClickListener(buttonClickEvent -> {
if (StringUtils.isNotEmpty(field.getValue())) {
Checkbox checkbox = new Checkbox(field.getValue());
buttons.add(checkbox);
field.setValue("");
}
});
add(
new H1("Control it!"),
buttons,
field,
button
);
}
public TextField getField() {
return field;
}

I figured out a way to avoid calling the getters
I made the Gui Application that should get data from the Vaadin a #Component too and #Autowired it into Main. This might be the wrong way, but it works.

Even though you can autowire in the Main class, if you want to use it as a bean you need to add the Spring #Component (or Vaadin's #SpringComponent) annotation, as well as #UIScope for it to return the right instance of the bean.

Related

Is there a way for #SpyBean to create spies for all types based on the interface type in a Spring Boot test?

I have a Spring Boot application where I would like to ensure that a list of decorators are verified to be executed. These decorators all extend from the same Abstract class, which in turn extend from the same interface, and they are autowired into a service class as a list of decorators. I would have thought that providing the #SpyBean(MyDecorator.class) at the class level of the test would have done the trick, but I got the error specifying that the decorator is not a spy. It looks like the MockitoPostProcessor class expects that we provide the individual concrete classes in the annotation as so #SpyBean(classes = {decorator1.class,decorator2.class}). I tried the latter, and it worked.
However, the issue that I have with this is that we have to add to this list every time we create a new decorator, which is not ideal. This is why I thought it makes sense to have the interface type be checked as well. Please let me know if there is a better way of doing this, or if I missed something. A thought that crossed my mind was to define my own post processor to wrap any bean from a defined type in a mockito spy, but I would like to check here first. Here is a skeleton definition of the classes to help you understand my dilemma.
MyDecorator.java
public interface MyDecorator{
public void decorate(SomeObject obj);
}
AbstractDecorator.java
public class AbstractDecorator implements MyDecorator{
//common decorator logic
}
Decorator1.java
#Component
public class Decorator1 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
Decorator2.java
#Component
public class Decorator2 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
DecorationService.java
#Service
public class DecorationService implements Service{
#Autowired
private List<MyDecorator> decoratorList;
public void processDecorators(){
//go through list of decorators and process some object
}
}
DecoratorServiceTest.java
#Runwith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
//#SpyBean(MyDecorator.class) //<-- This doesn't wrap the classes in a spy and errors out
#SpyBean(classes = {Decorator1.class, Decorator2.class}) //<-- This works
public class DecoratorServiceTest{
#Autowired
private List<MyDecorator> decoratorList;
#Test
public void testProcessDecorator(){
//verify that each decorator was processed
}
}
I posted a spring boot github issue here. Hopefully we would either see an improvement on it or we get an explanation as to why it is designed in this way.
I have a workaround in place that I'm using which is I've created a class that implements Spring's BeanPostProcessor interface, and I override the postProcessAfterInitialization method, and I check if the class is what I'm expecting, then I would wrap it in a mockito spy. Also, you would need to define the spring bean.
Here is a snippet of the class that I created.
public class SpyBeanPostProcessor<T> implements BeanPostProcessor{
/**
* The class type to spy on.
*/
private Class<T> typeToSpy;
/**
* Construct a SpyBeanPostProcessor with a class type to wrap
* as a {#link org.mockito.Spy}
* #param typeToSpy The class type to spy on.
*/
public SpyBeanPostProcessor(Class<T> typeToSpy) {
this.typeToSpy = typeToSpy;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (typeToSpy.isAssignableFrom(bean.getClass())){
return Mockito.spy(bean);
}else{
return bean;
}
}
}
I also needed to create a new spring bean that loads the BeanPostProcessor as shown below.
#Bean
public static SpyBeanPostProcessor decoratorSpyBeanPostProcessor(){
return new SpyBeanPostProcessor(MyDecorator.class);
}

How can #Validated work on common method?

In Springboot project, when I try to add #Validated on controller method, it worked.But now I want to add it on a common method, then failed.
Try to add #Validated on controller method, it worked
public class TaskValidator {
private static final Logger logger = LogManager.getLogger("TaskValidatorLogger");
public void validateTest(#Validated Test test) {
logger.info("Validate: {}", test.getName());
}
public static void main(String[] args) {
new TaskValidator().validateTest(new Test());
}
}
#Data
public class Test {
#NotNull(message = "name can not be null")
private String name;
}
It should throw an MethodArgumentNotValidException but not.
Spring MVC has the ability to automatically validate #Controller
inputs. In previous versions it was up to the developer to manually
invoke validation logic.
In the controller methods, springboot automatically binds any validators to the model and invoke it when the data is bound to the object.
But in your case , you are trying to validate an object in which case , springboot might not be automatically binding your validator to your model and call the validator.So, in that case, you will need to manually bind the object to the validator.
or you can manually invoke the validator on a bean like :
#AutoWired
Validator validator;
...
validator.validate(book);

Can't get UI in Spring

In the first Version of my program, I load MainScreen without problems on this way:
public class MyUI extends UI {
public void showMainView() {
addStyleName(ValoTheme.UI_WITH_MENU);
setContent(new MainScreen(MyUI.this));
mainScreen.setUI(MyUI.this);
}
public static MyUI get() {
return (MyUI) UI.getCurrent();
}
}
But now, I have changed it to:
#SpringUI
#Viewport("user-scalable=no,initial-scale=1.0")
#Theme("mytheme")
public class MyUI extends UI {
#Autowired
MainScreen mainScreen;
public void showMainView() {
addStyleName(ValoTheme.UI_WITH_MENU);
setContent(mainScreen);
getNavigator().navigateTo(getNavigator().getState());
}
public static MyUI get() {
return (MyUI) UI.getCurrent();
}
}
To get MainScreen via #Autowired, I had to clear the Constructor of MainScreen, but I don't know how to get now the UI I need for navigator.
I just get null in both ways.
#Component
#UIScope
public class MainScreen extends HorizontalLayout {
public MainScreen() {
UI ui = UI.getCurrent(); // is null
MyUI ui2 = MyUI.get(); // is null
CssLayout viewContainer = new CssLayout();
viewContainer.addStyleName("valo-content");
viewContainer.setSizeFull();
final Navigator navigator = new Navigator(ui, viewContainer);
// ...
}
I'm not familiar with all the frameworks you tagged. I'm going to address what I believe to be a logical error or possibly an incorrect design.
If you #Autowire the MainScreen into the UI you're allowing spring to guarantee whatever that is to be there. If you look at it using the preferred constructor injection it might be more obvious.
public class MyUI extends UI {
private MainScreen mainScreen;
#Autowired
public MyUI (MainScreen mainScreen) {
this.mainScreen = mainScreen;
}
}
MainScreen has already been instantiated at that point and could very well be in other parts of the code already so the mainScreen constructor shouldn't be dependent on MyUI.
How you deal with it is depends on what the desired behavior and relationship is. If you're needing the UI class in mainScreen but can't depend on it being available then you'll have to pass it in from the UI class itself (mainScreen.foo(this);).

Aspect isn't called

I am trying to call aspect after method execution. But is doesn't even go to code inside aspect.
Here is my configuration class:
#Configuration
#PropertySource("classpath:auditorium.properties")
#ComponentScan(basePackages = { "com.epam.spring.hometask.*" })
#EnableAspectJAutoProxy
public class AppConfig {
///Bean definitions
}
This is my aspect:
#Component
#Aspect
public class CounterAspect {
#AfterReturning(pointcut = "execution(* com.epam.spring.hometask.service.EventService.getByName(..))", returning = "event")
public void calculateAccessEventByNameCounter(Event event) {
System.out.println("Aspect running");
}
This is function signature I want to provide with my aspect:
public Event getByName(String name);
And test:
#Test
public void getByNameTest(){
Event actualEvent = eventService.getByName("Test event 1");
}
Nothing is printed after getByName call
Event event = new Event();
event.getByName();
Event should be a spring-managed bean for the aspects to be applied, i.e. you should obtain objects of type Event from the ApplicationContext as follows:
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("classpath:META-INF/Spring.xml");
Event event = context.getBean(Event.class);
or get them injected using #Resource or #Autowired annotations.
Then, the call to getByName() on event object will go through the AOP proxy created by Spring framework and the code in the AOP advices that match the method definition get executed.
Another way to get a spring-managed bean, while creating objects using new operator, is using the #Configurable annotation.

Spring #Required properties when creating #Bean annotated beans

I'm developing a Spring Boot application and am trying out using Java annotation-based bean creation (using #Configuration and #Bean) rather than the familiar old XML-based bean creation. I'm puzzled though. If I attempt to create a bean in XML but fail to set an #Required property I get a BeanInitializationException when the application context is created. In my trials so far with annotation-based bean creation though this does not seem to be the case.
For example:
public class MyClass {
...
#Required
public void setSomeProp(String val){
}
}
Then in Spring XML:
<bean class="MyClass"/>
This will blow up during application startup (and IntelliJ flags it) because the required property is not set. But the same does not seem to be true of this:
#Configuration
public class MyConfig {
#Bean
public MyClass myClass() {
return new MyClass();
}
}
This application starts up just fine even though the required property is not ever set. I must be missing something here, because this seems like a pretty key feature in Spring.
UPDATE
I did some digging & debugging and it turns out that the bean definition is somehow being flagged to skip checking that #Required fields are set. In the Spring class 'RequiredAnnotationBeanPostProcessor' the boolean method 'shouldSkip()' is returning true for beans created this way. When I used the debugger to force that method to return false bean creation did indeed blow up with the expected exception.
Seeing as I'm making a pretty basic Spring Boot application I'm inclined (as Zergleb suggests) to submit this as a bug.
UPDATE 2
Some further debugging has revealed that even if the field is getting set forcing the check still throws the same exception, as if it hadn't been set. So perhaps dunni is correct and there is no way for this to work with #Bean notation.
As you said I also could not get #Required to run as expected this may be a bug and needs to be reported. I have a few other suggestions that did work for me.
Class annotated with #Configuration
//With the bean set up as usual These all worked
#Bean
public MyClass myClass() {
return new MyClass();
}
When you annotate the class #Component and load using component scanning works as expected.(The component scanning part is important you either need your #Configuration class to either have #ComponentScan or perhaps remove #Configuration and replace with #SpringBootApplication and this will enable scanning for components without needing to wire them up using #Bean configs)
#Component // Added this
public class MyClass {
...
#Required //Failed as expected
public void setSomeProp(String val){
}
}
Use #Autowired(required=true) //Fails with BeanCreationException //No qualifying bean of type [java.lang.String] found for dependency
//No more #Component
public class MyClass {
...
#Autowired(required=true) //Fails
public void setSomeProp(String val){
}
}
#Autowired required=false //Does not crash
public class MyClass {
...
#Autowired(required=false) //Simply never gets called if missing
public void setSomeProp(String val){
}
}
#Value //Does not work if test.property is missing // Could not resolve placeholder 'test.property' in string value "${test.property}
public class MyClass {
#Value("${test.property}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp;
}
}
#Value with default value//Does not crash // When getSomeProp is called it returns "My Default Value"(Unless you have test.property=Anything in your application.properties file then it returns "Anything"
public class MyClass {
#Value("${test.property:My Default Value}")
String someProp;
//This getter is not neccesary neither is a setter
public String getSomeProp() {
return this.someProp; //Returns "My Default Value"
}
}
Inside your #Configuration file also fails if it cannot find anything to populate String someProp in the myClass method
#Bean
public MyClass myClass(String someProp) { //Fails being unable to populate this arg
MyClass myObj = new MyClass();
myObj.setSomeProp(someProp);
return ;
}
If course this won't work, since you create the object of MyClass yourself (new MyClass()), thus the annotations are not evaluated. If you create a bean with a #Bean method, the container will only make sure, that all dependencies are there (method parameters) and that the bean scope is adhered to, meaning if it's a singleton bean, only one bean is created per application context. The creation of the bean/object itself is solely the responsibility of the developer.
The equivalent of the xml <bean> tag is annotating the class with #Component, where the bean is created completely by the container, thus the annotations are evaluated.
As it is being said that when you are having your own #Configuration class where you are creating the bean by itself, #Required doesn't apply there.
When you already have a #Component, let Spring Boot do the component scan and at the required setter property you can add #Autowired and it will work fine.
Found this link on web- https://www.boraji.com/spring-required-annotation-example
For example:
I have a Component called Employee having Id and Name.
#Component
public class Employee {
int id;
String name;
public int getId() {
return id;
}
#Autowired
#Required
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have a Configuration class called AppConfig.java
#Configuration
public class AppConfig {
#Bean
public int getId() {
return 1;
}
}
So now we see, that component Employee needs an Id property for binding during startup, so I wrote bean method of type Integer, which will get autowired during runtime. If you do not write a bean of type Integer, it will result a BeanCreationException.
And here is my main class file.
#SpringBootApplication
public class SingletonApplication {
public static void main(String[] args) {
ApplicationContext ctx =
SpringApplication.run(SingletonApplication.class, args);
Employee emp = (Employee)ctx.getBean(Employee.class);
System.out.println(emp.getId());
}
}

Resources