Lets say we have a class
#RequestScope
public abstract class A {
int a;
}
and another class which extends the above class
#Service
public class B extends A {
public int getA () { return a; }
}
Is this class B's variable (that it is extending from A) is a request scoped variable?
UPD
I was going through the spring code, it says
/**
* Constant for the default scope name: {#code ""}, equivalent to singleton
* status unless overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
Also,
((AbstractBeanDefinition)((AnnotationConfigEmbeddedWebApplicationContext) ctx).
getBeanDefinition("b")).scope
returns "singleton"
but if I mark class B with #RequestScope this property changes to ""
which i assume is sigleton again
To be inheritable annotation has to be marked with #Inherited annotation. Take a look at the source code of #RequestScope:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Scope(WebApplicationContext.SCOPE_REQUEST)
public #interface RequestScope {
/**
* Alias for {#link Scope#proxyMode}.
* <p>Defaults to {#link ScopedProxyMode#TARGET_CLASS}.
*/
#AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
It's not marked with #Inherited. Therefore it doesn't affect subclasses. That means variable of class B from your example is not request scoped but singleton as it's supposed to be by default. You can find more details about predefined annotations here.
Related
Is it possible to achieve the following?
I want to determine the type of a class to be used at runtime with Spring Boot. Hence, I defined an interface MyInterface defining the required methods:
public interface MyInterface<C extends DocumentContext> {
MyResult<C> doForX(C documentContext);
MyResult<C> doForY(C documentContext);
}
An abstract base class implements this interface and provide some common methods:
#Slf4j
#Component
public abstract class BaseClass<C extends DocumentContext> implements MyInterface<C> {
private MyInterface myInterface;
...
public MyResult<C> doForX(C documentContext) { ... }
public Myresult<C> doForY(C documentContext) { ... }
}
This base class is the parent for two subclasses:
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "REGEX", matchIfMissing = false)
public class FirstClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
#RequiredArgsConstructor
#Slf4j
#Component
#ConditionalOnProperty(name = "sanitizerType", havingValue = "SAX", matchIfMissing = false)
public class SecondClass<C extends documentContext> extends BaseClass<C> implements<MyInterface<C> {
...override base methods
}
A class using one of the two concrete subclasses:
public class MyUsingClass extends AnotherClass {
public MyUsingclass(MyInterface<ADocumentContext> myInterfaceImpl, ...)
...
}
This compiles but when I try to run the application I get:
*************************** APPLICATION FAILED TO START
Description:
Parameter 1 of constructor in MyUsingClass required a bean of type
'MyInterface' that could not be found.
Action:
Consider defining a bean of type 'MyInterface' in your configuration.
In my application.properties I have:
# ... [SAX | REGEX]
#sanitizerType=REGEX
...and I have a Properties class:
#Value("${sanitizerType:REGEX}")
private SanitizerType sanitizerType;
, whereby SanitizerType is just:
public enum SanitizerType {
REGEX, SAX
}
You said you would like to "determine the type of a class to be used at runtime with Spring Boot".
I'm not sure if you want your "class to be used" change while your application is running depending on current condidionts or set once when the application boots and use it always until the app is running.
If you uncomment this line of your application.properties #sanitizerType=REGEX
Your code should work - but it simply defines the SanitizerType (hence your MyInterface implementation) once when you start your application.
Please note that by setting a private field of Properties class you don't set the property globally (at least not the way you showed in the question), but only set the private field value either according what's set in your properties file or (if value is missing in properties) to "REGEX".
If you want to use it in different places of your program you need to inject it differently
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);
}
I have problem with writing test cases using SPOCK. Could anyone please help me?
I have classes & interfaces like below,
//Helper class
public class ObjClass{
//Defining all property variables & corresponding getters & setters methods.
}
//Interface
public interface B{
//Declaring custom methods for Mongo repository.
public int getId();
}
public interface A extends MongoRepository<ObjClass, Serializable>, B{
//Defining some standard MongoRepository methods here
}
// Implementation Classes
public class Aimpl implements B{
//implementing all B interface methods
}
public class ctrlClass{
#Autowired
A aObj;
public int getIdValue(){
return aObj.getId();
}
}
And below is the corresponding SPOCK test cases:
class test extends Specification
{
ctrlClass obj1
A obj2 //interface class object
def setup(){
obj1 = new ctrlClass();
obj2 = new Aimpl(); //Creating object for interface using impl class.
obj1.aObj = obj2
}
def "test"(){
when:
def a = obj2.getIdValue()
then:
//validating some conditions here with 'a' value
}
}
Getting below error while executing above test case,
Cannot cast object Aimpl to class A.
The same above scenario is working fine with Spring #Autowired. But not in Spock.
*
Is there any alternate available for #Autowired in SPOCK? Please suggest me some solutions & your comments.
*
The problem you have is the ability of Spring to bind the interface with the related implementation.
If your interface has only one implementation and the single implementation has the annotation #Component with Spring's component scan enabled, than Spring framework success to infer the relationship between the interface and its implementation.
In case the component scan is not enabled, then the bean should be explicitly defined in your spring configuration file (such as application-config.xml).
The casting of Aimpl and A cannot succeed because the inheritance classes/interface are different.
You should change the code like the following:
public class ctrlClass{
#Autowired
Aimpl aObj;
public int getIdValue(){
return aObj.getId();
}
}
And in the test class make the following change:
A obj2 //interface class object
Should be changed to:
Aimpl obj2
fIn my app I'm using the Spring Security and have defined next classes.
public abstract class AbstractService {
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publist class UserService extends AbstractService {
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS_X)
publist class XService extends AbstractService{
}
I need #PreAuthorize annotation from the child class to be applied to the super class methods( for example: save()).Is there any way to achieve it by avoiding to override super class methods?
AbstractService will have more than one child( > 10) wherein each have own #PreAuthorize value.
You can try to use SPEL for that.
Because AFAIK, you must annotate methods or the superclass or the superclass itself, and the annotation must be a plain string (or a static final which is the same). But the string may contain SPEL expressions that will reference the target object. Example if only roles were used :
#PreAuthorize("hasAnyRole(#root.target.requiredRoles)")
public abstract class AbstractService {
public abstract String getRequiredRoles();
public void save(){
.....
}
}
#Service
#PreAuthorize(SpelAuthorityExpressions.SOME_KIND_OF_ACCESS)
publix class UserService extends AbstractService {
#Override
public String getRequiredRoles() {
return "ROLE_USER, ROLE_CLIENT";
}
....
}
As the condition is evaluated by SPEL at runtime, it will use the overriden getter and the list of roles can be defined in child class.
Without knowing what are your requirements for authorization expressions, I cannot be sure if that will do the trick, but I successfully use that for caching methods in a superclass, with keys depending on values in child classes.
Using xml, I was able to define a common xml file, where i can put common bean which are used for other differect condig file.
I move my config to psring java config, how to achieve this with java config?
let's say I have my common class as :
#Configuration
public class Common {
#Bean
public A a(){
return new A();
}
}
and I want to use it as
#Configuration
public class AConfig {
#Bean
public ABB abb(){
ABB abb = new ABB();
//TODO abb.set ????
return abb;
}
}
The TODO part is missing, I want to use the a() from the common class.
Is that possible?
The simplest approach is just to 'Autowire' in a private member like this:
#Configuration
public class AConfig {
#Autowire
private A myA;
#Bean
public ABB abb(){
ABB abb = new ABB();
abb.setA(myA); // or MUCH better, make the A member of ABB private final and overload a construtor
return abb;
}
}
The reason this works is that AConfig is a Bean too. It has to be constructed by the Spring Bean Factory. After construction, the Post Construction activities take place - one of those being processing post construction annotations such as Autowired. So 'myA' will be set before it is used in the #Bean annotated method.
From the #Import annotation Javadoc:
* <p>Provides functionality equivalent to the {#code <import/>} element in Spring XML.
* Only supported for classes annotated with {#code #Configuration} or declaring at least
* one {#link Bean #Bean} method, as well as {#link ImportSelector} and
* {#link ImportBeanDefinitionRegistrar} implementations.
*
* <p>{#code #Bean} definitions declared in imported {#code #Configuration} classes
* should be accessed by using {#link org.springframework.beans.factory.annotation.Autowired #Autowired}
* injection. Either the bean itself can be autowired, or the configuration class instance
* declaring the bean can be autowired. The latter approach allows for explicit,
* IDE-friendly navigation between {#code #Configuration} class methods.
*
* <p>May be declared at the class level or as a meta-annotation.
*
* <p>If XML or other non-{#code #Configuration} bean definition resources need to be
* imported, use {#link ImportResource #ImportResource}
I presume that if you import the #Configuration class itself, the "latter approach", you'd then just explicitly call the #Bean method on the imported class, e.g.
#Configuration
#Import(BarConfiguration.class)
public class FooConfiguration {
#Autowired
private BarConfiguration barConfiguration;
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setBar(barConfiguration.bar());
return foo;
}
}