How to avoid retrieving a Spring managed bean from the context - spring

My application receives messages, extracts data and persists the extracted data to a database. Data is received via a Apache Camel channel, added to a FIFO. The following code takes the next message from the FIFO and processes it. However, in order to do this it needs to get a bean from the Spring application context:
private static void dispatch(Message msg) {
if (msg == null) {
return;
}
// TODO: This really violates IoC.
DomainObjectFactory factory = (DomainObjectFactory) ApplicationContextProvider.getApplicationContext().getBean("domainObjectFactoryBean", DomainObjectFactory.class);
// do something with message
This is the service class:
#Service
public class ApplicationContextProvider implements ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(ApplicationContextProvider.class);
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return getCtx();
}
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
log.debug("Loading context");
ApplicationContextProvider.setCtx(ctx);
}
public static ApplicationContext getCtx() {
return ctx;
}
public static void setCtx(ApplicationContext ctx) {
ApplicationContextProvider.ctx = ctx;
}
}
reading a message from the FIFO:
void process(Object obj) {
Message msg = (Message) obj;
try {
Dispatcher.process(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
This is really weak code, but I can't work out how to avoid it? That is how to use Spring IoC to link the removal of a message from the FIFO to the message processing without having to retrieve the bean from the context.
Any advice / guidance appreciated

Naturally, your code would look a whole lot nicer, if you would use annotation or xml-based injection. This would be easier, if your dispatch method was not static. Still, you can inject Spring beans into static fields utilizing a MethodInvokingFactoryBean. Look here for more information:
How can I inject local variables of a static method inside an abstract class using Spring?

Related

Dependency injection in custom evaluator in logback in spring boot

I am writing a custom evaluator in which I want to autowire another bean. I am unable to do so as evaluator gets initialized by logger where as beans are initialized by spring context. Below is the sample of my code:
In logback-spring.xml:
<appender name="myAppender" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="com.package.CustomEvaluator">
<marker>FATAL</marker>
<interval>1000000</interval>
</evaluator>
</appender>
My custom evaluator:
#Slf4j
#Component
public class CustomEvaluator extends OnMarkerEvaluator {
#Autowired
private MyService myService;
#Override
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
\\logic goes here
}
}
I am getting object of MyService always as null(which is expected). Is there any work around for this?
It don't think its possible because the Evaluator being an internal logback abstraction is not managed / initialized by spring, so obviously spring can't autowire anything into the evaluator.
In addition note, that logback gets initialized even before application context starts.
Of course you could provide some global holder class for the application context and set the context to it in the main method, and then get the reference to it in this evaluator, something like this:
public class ApplicationContextHolder {
private static ApplicationContext context;
// call it in main() method
public static void setContext(ApplicationContext ctx) {context = ctx;}
public static ApplicationContext getContext() {return context;}
}
class CustomEvaluator extends OnMarkerEvaluator {
public boolean evaluate(ILoggingEvent event) throws EvaluationException {
ApplicationContext ctx = ApplicationContextHolder.getContext();
if(ctx == null) {return false;} // not yet initialized / accessible
MyService myService = ctx.getBean(MyService.class);
}
}
But all-in-all I believe its a very ugly solution.
As a suggestion, I think you should consider refactoring of the logic so that the decision of whether to send an email based on logging event will be taken in the application (which is, I assume, spring boot driven so you have an access to the MyService)
Given the current implementation:
public foo() {
LOGGER.info("This should be sent by email");
}
I suggest a part of application:
#Component
public class MyLogic {
#Autowired MyService myService;
public void foo() {
if(myService.shouldSend()) {
LOGGER.info("This should be sent by email");
}
}
}

How to get request in MyBatis Interceptor

I want to measure time of sql execution which will be run by MyBatis (Spring Boot project) and bind that with other request parameters, so I can get full info about performance issues regarding specific requests. For that case I have used MyBatis Interceptor on following way:
#Intercepts({
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
#Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMetricsMybatisPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
Object result = invocation.proceed();
stopwatch.stop();
logExectionTime(stopwatch, (MappedStatement) invocation.getArgs()[0]);
return result;
}
}
Now when it come to binding with request, I want to store those metrics in request as attribute. I have tried this simple solution to get request, but that was not working since request was always null (I have read that this solution won't work in async methods, but with MyBatis Interceptor and its methods I think that's not the case):
#Autowired
private HttpServletRequest request;
So, the question is how properly get request within MyBatis interceptor?
One important note before I answer your question: it is a bad practice to access UI layer in the DAO layer. This creates dependency in the wrong direction. Outer layers of your application can access inner layers but in this case this is other way round. Instead of this you need to create a class that does not belong to any layer and will (or at least may) be used by all layers of the application. It can be named like MetricsHolder. Interceptor can store values to it, and in some other place where you planned to get metrics you can read from it (and use directly or store them into request if it is in UI layer and request is available there).
But now back to you question. Even if you create something like MetricsHolder you still will face the problem that you can't inject it into mybatis interceptor.
You can't just add a field with Autowired annotation to interceptor and expect it to be set. The reason for this is that interceptor is instantiated by mybatis and not by spring. So spring does not have chance to inject dependencies into interceptor.
One way to handle this is to delegate handling of the interception to a spring bean that will be part of the spring context and may access other beans there. The problem here is how to make that bean available in interceptor.
This can be done by storing a reference to such bean in the thread local variable. Here's example how to do that. First create a registry that will store the spring bean.
public class QueryInterceptorRegistry {
private static ThreadLocal<QueryInterceptor> queryInterceptor = new ThreadLocal<>();
public static QueryInterceptor getQueryInterceptor() {
return queryInterceptor.get();
}
public static void setQueryInterceptor(QueryInterceptor queryInterceptor) {
QueryInterceptorRegistry.queryInterceptor.set(queryInterceptor);
}
public static void clear() {
queryInterceptor.remove();
}
}
Query interceptor here is something like:
public interface QueryInterceptor {
Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}
Then you can create an interceptor that will delegate processing to spring bean:
#Intercepts({
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }),
#Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}) })
public class QueryInterceptorPlugin implements Interceptor {
#Override
public Object intercept(Invocation invocation) throws Throwable {
QueryInterceptor interceptor = QueryInterceptorRegistry.getQueryInterceptor();
if (interceptor == null) {
return invocation.proceed();
} else {
return interceptor.interceptQuery(invocation);
}
}
#Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
#Override
public void setProperties(Properties properties) {
}
}
You need to create an implementation of the QueryInterceptor that does what you need and make it a spring bean (that's where you can access other spring bean including request which is a no-no as I wrote above):
#Component
public class MyInterceptorDelegate implements QueryInterceptor {
#Autowired
private SomeSpringManagedBean someBean;
#Override
public Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
// do whatever you did in the mybatis interceptor here
// but with access to spring beans
}
}
Now the only problem is to set and cleanup the delegate in the registry.
I did this via aspect that was applied to my service layer methods (but you can do it manually or in spring mvc interceptor). My aspect looks like this:
#Aspect
public class SqlSessionCacheCleanerAspect {
#Autowired MyInterceptorDelegate myInterceptorDelegate;
#Around("some pointcut that describes service methods")
public Object applyInterceptorDelegate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
QueryInterceptorRegistry.setQueryInterceptor(myInterceptorDelegate);
try {
return proceedingJoinPoint.proceed();
} finally {
QueryInterceptorRegistry.clear();
}
}
}

Spring PostConstruct of a container

How can I run some code inside a Spring Container after all beans has been loaded? I know I can use #PostConstruct for a single bean, but I would like to run that piece of code after all PostConstructs are called.
Is is possibile?
---UPDATE---
I tried to follow the ApplicationListener way, this is the implementation:
#Component
public class PostContructListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger log = LoggerFactory.getLogger(PostContructListener.class);
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Collection<Initializable> inits= contextRefreshedEvent.getApplicationContext().getBeansOfType(Initializable.class).values();
for (Initializable initializable : inits) {
try{
log.debug("Initialization {} ",initializable.getClass().getSimpleName());
initializable.init();
}catch(Exception e){
log.error("Error initializing {} ",initializable.getClass().getSimpleName(),e);
}
}
}
}
Applying "Initializable" interface to all services I got what I needed, how every this way I broke all autowires, I cannot understand why but seems to be connected to the new "Initializable" interface:
java.lang.IllegalArgumentException: Can not set com.service.MyService field com.controller.RestMultiController.myService to com.sun.proxy.$Proxy41
I think you need this.
public class SpringListener implements ApplicationListener<ContextRefreshedEvent>{
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent ) {
// do things here
}
}
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events

AOP using Spring Boot

I am using this Spring AOP code in my Spring Boot starter project in STS. After debugging this for some time I don't see any problem with the AspectJ syntax. The Maven dependencies are generated by STS for a AOP starter project. Is there a glaring omission in this code like an annotation ? The other problem could be with the AOP starter project or with the way I try to test the code in a #PostConstruct method.
I installed AJDT but it appears STS should show AspectJ markers in the IDE on its own. Right ? I don't see the markers. What other AspectJ debugging options are included in STS ? -Xlint is what I used in Eclipse/AJDT.
StateHandler.java
public class StateHandler<EVENTTYPE extends EventType> {
private State<EVENTTYPE> state;
private Event<EVENTTYPE> event;
public StateHandler(State<EVENTTYPE> state, Event<EVENTTYPE> event) {
this.state = state;
this.event = event;
}
public void handle( Event<EVENTTYPE> event ){
state = state.handle( event );
}
public State<EVENTTYPE> getState() {
return state;
}
}
DeviceLogger .java
#Aspect
#Component
public class DeviceLogger {
private static Logger logger = Logger.getLogger("Device");
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public void log() {
logger.info( "Logger" );
}
}
LoggerApplication.java
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
SpringApplication.run(LoggerApplication.class, args);
}
#PostConstruct
public void log(){
DeviceState s = DeviceState.BLOCKED;
StateHandler<DeviceEvent> sh = new StateHandler<DeviceEvent>( s,
Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
}
There are 3 obvious things wrong and 1 not so obvious wrong.
Your aspect is wrong and breaks proper method execution. When using an around aspect you must always return Object and use a ProceedingJoinPoint and call proceed() on that.
You are creating new instances of classes yourself, Spring, by default, uses proxy based AOP and will only proxy beans it knows.
In a #PostConstruct method it might be that proxies aren't created yet and that nothing is being intercepted
You need to use class based proxies for that to be enabled add spring.aop.proxy-target-class=true to your application.properties. By default JDK Dynamic Proxies are used which are interface based.
Fix Aspect
Your current aspect doesn't use a ProceedingJoinPoint and as such never does the actual method call. Next to that if you now would have a method that returns a value it would all of a sudden return null. As you aren't calling proceed on the ProceedingJoinPoint.
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
logger.info( "Logger" );
return pjp.proceed();
}
Create a bean to fix proxying and #PostConstruct
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(LoggerApplication.class, args);
StateHandler<DeviceEvent> sh = context.getBean(StateHandler<DeviceEvent>.class);
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
#Bean
public StateHandler<DeviceEvent> auditMessageStateHandler() {
return new StateHandler<DeviceEvent>(DeviceState.BLOCKED, Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
}
}
Add property to enable class proxies
In your application.properties in src\main\resources add the following property with a value of true
spring.aop.proxy-target-class=true

EclipseLink + JPA Guice Persist and Redeployments

I have an infrastructure based on EclipseLink + JPA Guice Persist
When I redeploy the application always I have caching problems with caching Entitys and I have to reboot the server (Oracle Weblogic 11g) .This problem is treated in a this post: https://bugs.eclipse.org/bugs/show_bug.cgi?id=326552 But, maybe is not a bug ¿?¿? ...
I managed to solve the problem as follows :
Originally I have centralized everything in a GuiceModule:
1.Create the module JPAPersist
2.Binding of a Initializer class thas invokes the persistenceService.start()
public class MyGuiceModule implements Module {
#Override
public void configure(final Binder binder) {
Properties props = _dbConnectionPropertiesForPool();
JpaPersistModule jpaModule = new JpaPersistModule(persistenceUnit);
jpaModule.properties(props);
binder.install(jpaModule);
binder.bind(JPAInitializer.class).asEagerSingleton();
}
public class JPAInitializer {
#Inject
public JPAInitializer(final PersistService service) {
service.start();
}
}
Everything works fine .... but as I said when redeploy remain cached instances
HOW DO I HAVE SOLVED?
I changed the method JPAInitializer
public static class JPAInitializer {
private static PersistService _persistenceService = null;
#Inject
public JPAInitializer(final PersistService service) {
_persistenceService = service;
_persistenceService.start();
}
public static void stop() {
_persistenceService.stop();
}
}
I created a method stop () that stops the service ..but WTF! I have been forced to save the the injected Persistence service in a static variable :((
From the guice / listener filter that is the entrypoint invoke the stop when the application is undeployed (onContextDestroyed)
public void contextDestroyed(ServletContextEvent servletContextEvent) {
JPAInitializer.stop();
}
Now, when i redeploy there is no cache issue or problem, and there is no need to restart the server
It works this way, but I do not know if it's all right to create a static instance PesistenceService., so i'm trying to find another way to invoke the stop.....
Any suggestion?
Found solution .
Create a inteface to handle Guice Persistence Service :
interface MyPersistenceServiceHandler {
public void start();
public void stop();
}
This will be used into the main DB Guice Module :
binder.bind(MyPersistenceServiceHandler .class)
.to(JPAPersistenceServiceControl.class)
.in(Singleton.class);
static class JPAPersistenceServiceControl
implements MyPersistenceServiceHandler {
private final PersistService _service;
#Inject
public JPAPersistenceServiceControl(final PersistService service) {
_service = service;
}
#Override
public void start() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.start();
}
#Override
public void stop() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.stop();
}
}
Get the instance in the RESTEndoint/Guice filter through Guice Injector.
jpaServiceHandler = _myGuiceInjector.getInstance(MyPersistenceServiceHandler .class);
Start the service on contextInitialized : jpaServiceHandler.start();
Stop the service on contextDeproyed : jpaServiceHandler.stop();

Resources