practical problem
In the project, report export involves the same query in two different methods of the same class. data base One method is to get the content, the other is to get the number of bars, which is similar to this.
@Service
public class MyReportExporter extends AbstractReportExporter{
@Override
protected DataResp getData(Param param) {
List records = myService.queryList(param);//Query db
return wrapResp(records);
}
@Override
protected int getCount(Param param) {
return myService.queryList(param).size();//Query db
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Because it is the uniform processing of inherited parent classes, there is no way to optimize this step alone. The getCount method is called many times during the unified processing of the parent class, so that the database needs to be queried many times for each processing.
This is the idea that private global variables can be used to store query results.
Using prototypes
stay spring In @Service, by default, they are singletons. With private global variables, if you don't want to affect the next request, you need to use the prototype pattern @Scope("prototype").
The so-called singleton is that Spring's IOC mechanism only creates one instance of this class. Each request will be processed with the same instance. Therefore, if there is a global variable, the value of this request will definitely affect the value of the variable at the next request.
The prototype pattern refers to an instance of this class that is recreated each time it is invoked, similar to our own new object instance.
Looking at @Scope, we can see that the default mode is singleton
public @interface Scope {
String value() default ConfigurableBeanFactory.SCOPE_SINGLETON;
...
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
This class can be set to prototype mode in the following way
@Service
@Scope("prototype")
public class MyReportExporterextends AbstractReportExporter{
...
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
prototype trap
After the above changes, the operation found that it did not take effect, is still an example. This means that adding only one @Scope annotation is not enough.
In the controller layer that calls the change service, it is injected as follows:
@Autowired
private MyReportExporter myReportExporter;
- 1
- 2
- 1
- 2
Controller is also the default singleton, so only one controller object is instantiated, in which the MyReportExporter object dependent on injection is instantiated only once.
If you don't want to change the controller singleton mode, you can modify it as follows:
Instead of using @Autowire, use getBean:
private static ApplicationContext applicationContext;
MyReportExporter myReportExporter = applicationContext.getBean(MyReportExporter.class);
- 1
- 2
- 1
- 2
You can write a Spring factory class yourself, as follows:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.quhuhu.cesar.common.utils.LogUtils;
public class SpringBeanFactory implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Get the object of a Bean
*/
public static <T> T getBean(Class<T> clazz) {
try {
return applicationContext.getBean(clazz);
} catch (Exception e) {
LogUtils.errorMail("Spring getBean:" + clazz, e);
}
return null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
Then, it is invoked in the following way:
SpringBeanFactory.getBean(MyReportExporter.class).doSth()
- 1
- 1
After modification, run OK to achieve the desired results.
Summary
The default object of dependency injection in Spring is a singleton form, and the @Scope("prototype") annotation can change it to a prototype mode.
The prototype mode will take effect only when the object of the underlying layer (such as service layer) is changed as a prototype and the invocation mode of the upper layer (such as controller layer) is changed.