spring aop log full object before deleteById - spring

in my Project I am using spring Aop
in before deleteById I want log full object
but in joinPoint.getArgs() return id only not full object
can help me?
public void deletePointcut(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
}

Related

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 way of Javassist

What is the Spring way of code piece which is written in Javassist. I know that Spring is using CGLib but I am sure that there are some useful good practices to follow for spring world.
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Dog.class);
factory.setFilter(
new MethodFilter() {
#Override
public boolean isHandled(Method method) {
return Modifier.isAbstract(method.getModifiers());
}
}
);
MethodHandler handler = new MethodHandler() {
#Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
System.out.println("Handling " + thisMethod + " via the method handler");
return null;
}
};
Dog dog = (Dog) factory.create(new Class<?>[0], new Object[0], handler);
dog.bark();
dog.fetch();
Which produces this output:
Woof!
Handling public abstract void mock.Dog.fetch() via the method handler
Edit:
Currently I am using CGLib Enhancer which is included in Spring 3.2.x and I am in doubt about a convenient way and best practices.
Edit:
I have to say that my proxy classes are not spring beans. They are not managed by Spring.
In Spring you would do such a thing with Spring AOP. It's a much more high-level approach, which under the hood uses either CGLIB or JDK proxies, depending on configuration.
Here's a sample aspect:
#Aspect
public class LoggingAspect{
static final Logger LOG = LoggerFactory.getLogger(LoggingAspect.class);
#Pointcut("call(* *.*(*)")
public void methodCall(){}
#Before("methodCall()")
public void logMethodCall(Joinpoint jp){
LOG.debug("About to call method {} with args {}", jp.getSignature(), jp.getArgs());
}
}
The drawbacks are: this only works on public methods of Objects that are managed by Spring.

Can not get param from json request when using spring aop

I am using spring AOP to check permission
#Component
#Aspect
public class PermissionManager {
#Around(value = "#annotation(requiredPermission) && args(id,..)", argNames = "id,requiredPermission")
public Object checkCanViewFile(ProceedingJoinPoint pjp, String id, RequiredPermission permission) throws Throwable {
...
}
}
Controller
#RequiredPermission(RequiredPermission.OperationType.editProject)
#RequestMapping("/searchFile")
public #ResponseBody
WebFile search(String id, String word) throws TokenExpiredException, FetchException {
...
}
It works on spring mvc test but can not working on real environment. the value of 'id' is null, I doubt spring AOP get this method before jackson objectmapper, is it right? How can fix it?

How can I log the JSON response of Spring 3 controllers with #ResponseBody in a HandlerInterceptorAdapter?

I have controllers that return JSON to the client. The controllers methods are marked using mvc annotation such as:
#RequestMapping("/delete.me")
public #ResponseBody Map<String, Object> delete(HttpServletRequest request, #RequestParam("ids[]") Integer[] ids) {
Spring knows to return JSON since Jackson is on the class path and the client is requesting a JSON response. I would like to log the response of these requests and all other controllers. In the past I have used an interceptor to do this. However, I got the response body from the ModelAndView. How can I get the response body in the inteceptor now that I'm using #ResponseBody? Specifically, how can I get the response body in this method?
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
You can log everything by using CustomizableTraceInterceptor
you can either set it in your application context xml config and use AOP: (log level Trace)
<bean id="customizableTraceInterceptor"
class="org.springframework.aop.interceptor.CustomizableTraceInterceptor">
<property name="exitMessage" value="Leaving $[methodName](): $[returnValue]" />
</bean>
or you can completly customize it by implementing it in Java and use the method setExitMessage():
public class TraceInterceptor extends CustomizableTraceInterceptor {
private Logger log = LoggerFactory.getLogger("blabla");
#Override
protected void writeToLog(Log logger, String message, Throwable ex) {
//Write debug info when exception is thrown
if (ex != null) {
log.debug(message, ex);
}
....
}
#Override
protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger) {
return true;
}
#Override
public void setExitMessage(String exitMessage) {
.... //Use PlaceHolders
}
}
and use the placeholders such as '$[returnValue]'. You can find the complete list in the spring api documentation.
EDIT: Also, if you want to get the value of your #ResponseBody in another interceptor, I think it's not possible until version > 3.1.1. Check this issue: https://jira.springsource.org/browse/SPR-9226

Managed beans with custom ViewScope

I'm doing a Web application using Spring 3.1.0.RELEASE, JSF 2.x, JPA 2 with Hibernate Provider. I use PrettyFaces 3.3.2 for friendly URL. The application run on Tomcat 6.35 .
I wanted to use the Jsf ViewScope so I decided to follow the implementation found on the web : http://comdynamics.net/blog/109/spring3-jsf2-view-scope/
public class ViewScope implements Scope {
private static final Logger logger = LoggerFactory.getLogger(ViewScope.class);
#Override
public Object get(String name, ObjectFactory objectFactory) {
final Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
Object instance = viewMap.get(name);
if (instance == null) {
instance = objectFactory.getObject();
viewMap.put(name, instance);
}
return instance;
}
#Override
public Object remove(String name) {
logger.debug("ViewScope::remove {}", name);
return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
}
#Override
public String getConversationId() {
return null;
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
//Not supported
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
}
I notice that #PreDestroy are not called on them like show this question #PreDestroy never called on #ViewScoped.
Does it mean that the Managed beans with ViewScope are never destruct ? Which conduct to memory leak. Should we use this scope so?
It's only happen with custom Viewscope on Spring or also on Mojarra ?
Thanks.
Problem is incorrect implementaiton of view scope. It is creates Spring bean objectFactory.getObject(); but never destroy it.
To solve it - check correct implementation with support for registerDestructionCallback.
BWT, current Mojjara implementation will not call #PreDestory on your bean too.
But it will free bean instance at least.
I tried the work around for Jsf view scope bean memory leaks using spring custom view scope. It works for both Jsf 2.1 & 2.2.Try the code in below link.
Memory leak with ViewScoped bean?

Resources