how does use cglib correctly - spring

I am trying to understand cglib. But I am very confused by the below results. Can somebody please help explain it?
When using #Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
#Configuration
#Data
#Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DemoCGLIB {
private int counter;
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DemoCGLIB.class);
DemoCGLIB bean1 = context.getBean(DemoCGLIB.class);
bean1.setCounter(2);
System.out.println(bean1.getCounter());
DemoCGLIB bean2 = context.getBean(DemoCGLIB.class);
System.out.println(bean2.getCounter());
}
}
singleton bean instance is created and both bean1 and bean2 refer to the same instance. But counter is 0 even after setCounter(2)
When using #Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
#Configuration
#Data
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DemoCGLIB {
private int counter;
public static void main(String... strings) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DemoCGLIB.class);
DemoCGLIB bean1 = context.getBean(DemoCGLIB.class);
bean1.setCounter(2);
System.out.println(bean1.getCounter());
DemoCGLIB bean2 = context.getBean(DemoCGLIB.class);
System.out.println(bean2.getCounter());
}
}
bean1 and bean2 are different. There is no singleton instance created.

In 1st test using
#Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
CglibAopProxy is being used. setter/getter is done in proxy object.
xxxBean$$EnhancerBySpringCGLIB
CGLIB$CALLBACK
CglibAopProxy$DynamicAdvisedInterceptor
When printing out the beans, there are
demoCGLIB : class com.example.demo.bean.DemoCGLIB$$EnhancerBySpringCGLIB$$d34f96ce
scopedTarget.demoCGLIB : class com.example.demo.bean.DemoCGLIB$$EnhancerBySpringCGLIB$$ce3d6945
In 2nd test using #Configuration, some kind of cglib proxy was created(probably it is to handle Full mode vs Lite mode). It is not AOP proxy. Update is done on the real object.
xxxBean$$EnhancerBySpringCGLIB
CGLIB$CALLBACK
ConfigurationClassEnhancer$BeanMethodInterceptor
ConfigurationClassEnhancer$BeanFactoryAwareMethodInterceptor
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) makes it unique each time. Update is done in real object. So each object can have different value.

Related

where does spring boot set proxy as CGLib [duplicate]

Recently i found spring documentation page that says:
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used.
But it doesn't seem to be the case in my application. I wanted to write a small benchmark to compare the performance of both types of proxying.
There are two similar classes. Both have one method annotated with the #Transactional annotation. One class implements the interface and the other does not:
#Service
public class Cglib {
#Transactional
public void method() {}
}
public interface Dynamic {
void method();
}
#Service
public class DynamicImpl implements Dynamic {
#Override
#Transactional
public void method() {}
}
And based on the documentation for the first class, a CGLIB proxy should be created, and for the second, a JDK dynamic proxy.
But in my case CGLIB was used for both classes:
#Component
#RequiredArgsConstructor
public class Runner implements ApplicationRunner {
private final Cglib cglib;
private final Dynamic dynamic;
#Override
public void run(ApplicationArguments args) {
System.out.println(cglib.getClass());
System.out.println(dynamic.getClass());
}
}
class com.example.demo.proxy.cglib.Cglib$$EnhancerBySpringCGLIB$$767ff22
class com.example.demo.proxy.dynamic.DynamicImpl$$EnhancerBySpringCGLIB$$20a564d6
There are no additional configurations in the application. Only #SpringBootApplication class generated via spring initializr
Am I doing something wrong? The code was run on Spring Boot 2.7.2 and JDK 17.
That is due to spring-boot autoconfiguation:
#Configuration(proxyBeanMethods = false)
#ConditionalOnBean(TransactionManager.class)
#ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
#Configuration(proxyBeanMethods = false)
#EnableTransactionManagement(proxyTargetClass = false)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
public static class JdkDynamicAutoProxyConfiguration {
}
#Configuration(proxyBeanMethods = false)
#EnableTransactionManagement(proxyTargetClass = true)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
it turns #EnableTransactionManagement(proxyTargetClass = true) (Indicate whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false)) when the property spring.aop.proxy-target-class is not set (matchIfMissing = true)

I have problem with #Bean WebDriver and How run sevral tests using #Autowrited

Please, promt me. The main idea is create project with Selenium + Spring Boot.
I created it, created test#1 and test#2. If I run test#1, it is okay, after test#1 I want to run test#2, but doing it I have a probem with driver. If I am not mistaken, the reason is #Bean, I created WebDriver using #Bean. I think that problem is the Autowrited Bean with WebDriver, Pls tell me, How create Autowrited Bean for several test.
enter image description here
My Code:1) WebDriver config with Bean
#Configuration
public class WebDriverConfig {
#Bean
#ConditionalOnMissingBean
#ConditionalOnProperty(name = "browser", havingValue = "chrome")
#Primary
public WebDriver chromeDriver() {
WebDriverManager.chromedriver().setup();
return new ChromeDriver();
}
2)BasePage
#Component
public abstract class BasePage {
#Autowired
protected WebDriver driver;
protected final static long WAIT_TIME = 20;
#PostConstruct
private void init() {
PageFactory.initElements(this.driver, this);
}
3)My HomePage with test methods
#Component
public class HomePage extends BasePage {
#FindBy(xpath = "//a[#class='login_btn circle']")
private WebElement singIn;
#FindBy(xpath = "//*[#id='login_input1']")
private WebElement inputLogin;
#FindBy(xpath = "//*[#id='login_input2']")
private WebElement inputPassword;
#FindBy(css = "#login_submit")
private WebElement loginSubmit;
#FindBy(xpath = "//a[.//*[#id='anime_id_17']]")
private WebElement tg;
#FindBy(xpath = "//a[.//*[#id='anime_id_12']]")
private WebElement codeGias;
public HomePage goToHomePage(String url) {
driver.get(url);
waitForPageLoadComplete(WAIT_TIME);
return this;
}
public HomePage goToLogin(String name, String password) {
singIn.click();
waitForPageLoadComplete(WAIT_TIME);
inputLogin.clear();
inputLogin.sendKeys(name);
inputPassword.clear();
inputPassword.sendKeys(password);
loginSubmit.click();
waitForPageLoadComplete(WAIT_TIME);
tg.click();
waitForPageLoadComplete(WAIT_TIME);
return this;
}
public HomePage test(){
waitForPageLoadComplete(WAIT_TIME);
codeGias.click();
waitForPageLoadComplete(WAIT_TIME);
return this;
}
My BaseTest
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = SpringSelApplication.class)
public class BaseTest {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
public WebDriver driver;
#Autowired
public ApplicationContext applicationContext;
#BeforeEach
public void setup() {
driver.manage().window().maximize();
}
#AfterEach
public void teardown() {
driver.quit();
}
}
Consider the following fixes:
You don't have to use #ExtendWith, the chances are that you're running a reasonably recent version of spring boot and #SpringBootTest is already annotated with #ExtendWith annotation internally
A BasePage class, being abstract is not a #Component by itself.
When using a #SpringBootTest, make sure that you pass the class of the configuration that loads the webdriver. Spring boot, when sees a parameter to the #SpringBootTest doesn't load the whole application and instead loads whatever the the specified configuration class defines. Also since this way talks about "specific" configuration class to be loaded, it stops obeying the usual component scanning rules, so your web page might not be loaded, after all you're mixing configuration styles (you have both classes annotated with #Configuration and those using declarative approach - annotated with #Component)
Consider using #ContextConfiguration instead of #SpringBootTest if you have a plain case of configuration loading and don't really load the whole application

Spring Boot: use autowired constructor with class from configuration file

I have a Spring Boot 2.3 application with a controller:
#RestController
public class StatusController {
private final ServerStatusCheck serverStatusCheck;
private final ServerStatusMapper serverStatusMapper;
#Autowired
public StatusController(AService aService, ServerStatusMapper serverStatusMapper) {
this.serverStatusCheck = aService;
this.serverStatusMapper = serverStatusMapper;
}
// (...)
}
The class AService implements the interface ServerStatusCheck. There is also a BService class, also implementing ServerStatusCheck interface.
What I need to do: the injected AService object should be configurable in a configuration file, so that the service injected is either "AService" or "BService", depending on the configuration file values. What is the best way to achieve this using Spring Boot? If possible, I would like to keep the constructor-based autowiring (instead of field-based autowiring).
You can create the different beans in a configuration class with condition like https://reflectoring.io/spring-boot-conditionals/
#Configuration
public class ServiceConfiguration {
#ConditionalOnProperty(value="service.a.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckA() {
return new AService();
}
#ConditionalOnMissingBean
#ConditionalOnProperty(value="service.b.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckB() {
return new BService();
}
}
and then wire the bean into the constructor

Mapstruct - How can I inject a spring dependency in the Generated Mapper class

I need to inject a spring service class in the generated mapper implementation, so that I can use it via
#Mapping(target="x", expression="java(myservice.findById(id))")"
Is this applicable in Mapstruct-1.0?
As commented by brettanomyces, the service won't be injected if it is not used in mapping operations other than expressions.
The only way I found to this is :
Transform my mapper interface into an abstract class
Inject the service in the abstract class
Make it protected so the "implementation" of the abstract class has access
I'm using CDI but it should be the samel with Spring :
#Mapper(
unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring",
uses = {
// My other mappers...
})
public abstract class MyMapper {
#Autowired
protected MyService myService;
#Mappings({
#Mapping(target="x", expression="java(myservice.findById(obj.getId())))")
})
public abstract Dto myMappingMethod(Object obj);
}
It should be possible if you declare Spring as the component model and add a reference to the type of myservice:
#Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
That mechanism is meant for providing access to other mapping methods to be called by generated code, but you should be able to use them in the expression that way, too. Just make sure you use the correct name of the generated field with the service reference.
Since 1.2 this can be solved with a combination of #AfterMapping and #Context.. Like this:
#Mapper(componentModel="spring")
public interface MyMapper {
#Mapping(target="x",ignore = true)
// other mappings
Target map( Source source, #Context MyService service);
#AfterMapping
default void map( #MappingTarget Target.X target, Source.ID source, #Context MyService service) {
target.set( service.findById( source.getId() ) );
}
}
The service can be passed as context.
A nicer solution would be to use an #Context class which wrap MyService in stead of passing MyService directly. An #AfterMapping method can be implemented on this "context" class: void map( #MappingTarget Target.X target, Source.ID source ) keeping the mapping logic clear of lookup logic. Checkout this example in the MapStruct example repository.
What's worth to add in addition to the answers above is that there is more clean way to use spring service in mapstruct mapper, that fits more into "separation of concerns" design concept, called "qualifier". Easy re-usability in other mappers as a bonus.
For sake of simplicity I prefer named qualifier as noted here http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers
Example would be:
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.springframework.stereotype.Component;
#Component
#Mapper
public class EventTimeQualifier {
private EventTimeFactory eventTimeFactory; // ---> this is the service you want yo use
public EventTimeQualifier(EventTimeFactory eventTimeFactory) {
this.eventTimeFactory = eventTimeFactory;
}
#Named("stringToEventTime")
public EventTime stringToEventTime(String time) {
return eventTimeFactory.fromString(time);
}
}
This is how you use it in your mapper:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
#Mapper(componentModel = "spring", uses = EventTimeQualifier.class)
public interface EventMapper {
#Mapping(source = "checkpointTime", target = "eventTime", qualifiedByName = "stringToEventTime")
Event map(EventDTO eventDTO);
}
I am using Mapstruct 1.3.1 and I have found this problem is easy to solve using a decorator.
Example:
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring")
#DecoratedWith(FooMapperDecorator.class)
public interface FooMapper {
FooDTO map(Foo foo);
}
public abstract class FooMapperDecorator implements FooMapper{
#Autowired
#Qualifier("delegate")
private FooMapper delegate;
#Autowired
private MyBean myBean;
#Override
public FooDTO map(Foo foo) {
FooDTO fooDTO = delegate.map(foo);
fooDTO.setBar(myBean.getBar(foo.getBarId());
return fooDTO;
}
}
Mapstruct will generate 2 classes and mark the FooMapper that extends FooMapperDecorator as the #Primary bean.
I can't use componentModel="spring" because I work in a large project that doesn't use it. Many mappers includes my mapper with Mappers.getMapper(FamilyBasePersonMapper.class), this instance is not the Spring bean and the #Autowired field in my mapper is null.
I can't modifiy all mappers that use my mapper. And I can't use particular constructor with the injections or the Spring's #Autowired dependency injection.
The solution that I found: Using a Spring bean instance without using Spring directly:
Here is the Spring Component that regist itself first instance (the Spring instance):
#Component
#Mapper
public class PermamentAddressMapper {
#Autowired
private TypeAddressRepository typeRepository;
#Autowired
private PersonAddressRepository personAddressRepository;
static protected PermamentAddressMapper FIRST_INSTANCE;
public PermamentAddressMapper() {
if(FIRST_INSTANCE == null) {
FIRST_INSTANCE = this;
}
}
public static PermamentAddressMapper getFirstInstance(){
return FIRST_INSTANCE;
}
public static AddressDTO idPersonToPermamentAddress(Integer idPerson) {
//...
}
//...
}
Here is the Mapper that use the Spring Bean accross getFirstInstance method:
#Mapper(uses = { NationalityMapper.class, CountryMapper.class, DocumentTypeMapper.class })
public interface FamilyBasePersonMapper {
static FamilyBasePersonMapper INSTANCE = Mappers.getMapper(FamilyBasePersonMapper.class);
#Named("idPersonToPermamentAddress")
default AddressDTO idPersonToPermamentAddress(Integer idPerson) {
return PermamentAddressMapper.getFirstInstance()
.idPersonToPermamentAddress(idPersona);
}
#Mapping(
source = "idPerson",
target="permamentAddres",
qualifiedByName="idPersonToPermamentAddress" )
#Mapping(
source = "idPerson",
target = "idPerson")
FamilyDTO toFamily(PersonBase person);
//...
Maybe this is not the best solution. But it has helped to decrement the impact of changes in the final resolution.

Why does Springs #Required not work when configured by annotations

I wonder if there is a way to get #Required working when doing the configuration by annotations. I turned my configuration up-and-down and back again but nothing seems to work for me. I'm using Spring 3.1
My basic configuration looks like this:
#Configuration
public class SpringConfig {
#Bean
public MailSender mailSender() {
MailSender MailSender = new MailSender();
// mailSender.setBean(dlMailSender);
return mailSender;
}
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
// setting som props
return myBean;
}
}
MailSender is here:
#Configurable
public class MailSender {
private MyBean myBean;
#Required
public void setMyBean(MyBean myBean) {
this.myBean = myBean;
}
}
I'm testing it with this junit:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringConfig.class }, loader = AnnotationConfigContextLoader.class)
public class MailSenderTest {
#Test
public void test_main_beans_exists() {
// when then given
}
}
Thanks for any help
Short answer - this is not even theoretically possible.
When using XML-based, bean definitions with their dependencies are completely managed by application context. Spring is able to check, what is being set and what is not being set.
When using annotation-based configuration, you are setting the dependencies yourself. There is no way how Spring can even know what you are doing with the bean before returning it from the factory method.
If you want to check whether the bean is correctly initialized, use InitializingBean or #PostConstruct and implement self-checking method. Spring is doing this regularly in its own beans.

Resources