Question: How can I properly create class reflection, if class constructor contains heir class argument?
I have:
interface Car {}
class SportCar implement Car{}
class CarService {
public CarService(String str, Car car) {
...
}
}
But, when I try to do:
Class c = Class.forName("vbspring.model.CarService");
Class[] paramTypes = {String.class, SportCar.class};
Constructor constr = c.getDeclaredConstructor(paramTypes);
it throws: java.lang.NoSuchMethodException: vbspring.model.CarService.(java.lang.String, vbspring.model.SportCar)
P.S. My .xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="carService" class="model.CarService">
<constructor-arg value="Car Rental Service"/>
<constructor-arg ref="sportCar"/>
<property name="startCar"/>
</bean>
<bean id="sportCar" class="model.SportCar"/>
</beans>
EDIT: I try to write something like my own Spring Framework. I must create not only classes that belongs to Car hierarchy, so I try to write universal method that can create class of arbitrary type.
I can't write: Class[] paramTypes = {String.class, Car.class};
I must write something universal, like:
paramTypes[index++] = obj.getClass();
where obj - is SportCar or Car bean pulled from .xml by parser
The errors java.lang.NoSuchMethodException: vbspring.model.CarService.(java.lang.String, vbspring.model.SportCar) states you have no constructor like:
public CarService(String, SportCar)
which you don't. You have:
public CarService(String, Car) {
Change your code to
Class c = Class.forName("vbspring.model.CarService");
Class[] paramTypes = {String.class, Car.class}; // the constructor takes Car not SportsCar
Constructor constr = c.getDeclaredConstructor(paramTypes);
Then when you call the constructor to get a new instance, you can pass a SportsCar instance or any other Car instance for that matter.
constr.newInstance("sport", new SportsCar());
// or
constr.newInstance("a jeep", new Jeep());
// or
constr.newInstance("long car", new Limousine());
assuming Jeep and Limousine implement Car.
Related
i have this below code .
advice on setter is not triggered even though setter is called.
i can see it in the console
if i do advice on String getName() everything works just fine.
but its not working on setter public void setName(String name).
spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy />
<bean id="cust" class="aspectJ.Customer">
<property name="name" value="logging" />
</bean>
<bean id="aspect" class="aspectJ.LoggingAspect"/>
</beans>
Logging ASPECT
#Aspect
public class LoggingAspect {
#Before("allgetters()")
public void printAspect() {
System.out.println("Aspect Running");
}
#Before("allgetters()")
public void printAspectTwo() {
System.out.println("Aspect TWO Running");
}
#Pointcut("execution(public void setName(*))")
public void allgetters() {
}
}
CUSTOMER CLASS
package aspectJ;
public class Customer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("SETTER CALLED");
}
}
Main Class
public class MainClass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Customer obj = (Customer) context.getBean("cust");
}
}
Your aspect signature is wrong.
* will match a singular field
.. will match zero or multiple fields
Example 1
com.stackoverflow.*.Customer.setName(..)
Matches all packages starting with com.stackoverflow and ending with Customer. The wildcard will only match one package name. The method can accept zero or more arguments. Below are three examples of what it will match:
com.stackoverflow.question.Customer.setName()
com.stackoverflow.question.Customer.setName(String arg1)
com.stackoverflow.question.Customer.setName(String arg1, String arg2)
Example 2
com..Customer.setName(*, *)
Match all packages starting with com and ending with Customer. Accept methods with two arguments of any type. Below are two examples of what it will match. Notice that the wildcard will accept any number of packages.
com.example.Customer.setName(String arg1, Object arg2)
com.stackoverflow.question.Customer.setName(Integer arg1, Double arg2)
You should change your allgetters() to the following:
#Pointcut("execution(public void setName(..))")
public void allgetters() {
}
Spring AOP only works with beans managed by Spring. A bean is not managed until it is initialized, regardless if it is defined in Java or XML.
//The Customer object returned by this method is managed.
//The Customer object within the method is not managed
#Bean
public Customer createCustomer(){
//This is a pure Java object
Customer customer = new Customer();
//The object is not yet managed. This method call will therefore never be intercepted by your Pointcut.
customer.setName(“John Doe”);
return customer;
}
Quote from the documentation:
Spring AOP only supports method execution join points for Spring
beans, so you can think of a pointcut as matching the execution of
methods on Spring beans.
Just Posting what user #javamusings said :
The advice is called only when the setter is called in the Java class. It is not called when the property is initialized in the aspectj.xml
I know you can bind get request parameters to a pojo like:
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
Using a pojo like:
public class ReservationCriteria {
String hotelName;
DateRange reservationDateRange;
//getters-setters omitted
}
With a request: /reservation?hotelName=myHotel
myHotel will be bound to hotelName in ReservationCriteria object.
But how can I bind parameters to the nested object DateRange? Which defined like:
public class DateRange {
Date from;
Date to;
//getters-setters omitted
}
Is there a URL pattern which allows that kind of binding something like:
/reservation?hotelName=myHotel&reservationDateRange={fromDate=14.04.2016,toDate=15.04.2016}
Or do I have to declare seperate request parameters and bind them manually?
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(
ReservationCriteria criteria,
#RequestParam Date from,
#RequestParam Date to)
DateRange range = new DateRange();
range.setFrom(from);
range.setTo(to);
criteria.setDateRange(range);
return service.loadReservations(criteria);
}
I would prefer not to modify ReservationCriteria class because it is used in many other projects, which would cause alot of refactoring to be made.
Since at least Spring 4 you can pass in nested objects separated with "." in the url.
In the OP case it would be for query parameters:
?reservationDateRange.from=2019-04-01&reservationDateRange.to=2019-04-03
This assumes that Date can be parsed from the given string. This may not work to an arbitrary level of nesting but I've tested it works with one additional nested object.
When you pass a POJO as container of data, Spring use the name of the properties for build the query string and with the data that you pass build the pojo through an appropriated converter. This works for planar pojo or in other words without nesting, for this purpose you have provide the your converter. for this reason you cold have a think like below:
public class ReservationCriteria {
String hotelName;
Date from;
Date to;
//getters-setters omitted
}
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
/reservation?hotelName=value&from=val&to=val
in this way you can benefit of standard converter of SpringMVC.
the your attempt to use a sort of json for codificate the inner object didn't work because Spring by default in query string don't understand this presentation you have provide a converter for this purpose.
Update for answer to Ben's comment:
If you want implement a custom Converter you had implements the org.springframework.core.convert.converter.Converter<S, T> and then register the your new Converter on the Spring Conversion Service.
On xml configuration you can use FormattingConversionServiceFactoryBean and register it on mvc namespace like below:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<mvc:annotation-driven conversion-service="conversionService"/>
<context:component-scan base-package="com.springapp.mvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<util:list>
<bean class="com.springapp.mvc.DateRangeToStringConverter"/>
<bean class="com.springapp.mvc.StringToDateRangeConverter"/>
</util:list>
</property>
</bean>
</beans>
on java config you can extends WebMvcConfigurerAdapter and add you bena like below:
#Configuration
#EnableWebMvc
public class YourWebConfigurationClass extends WebMvcConfigurerAdapter{
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(yourConverter());
}
...
}
the your converter can be like below:
public class DateRangeToStringConverter implements Converter<DateRange,String> {
#Override
public String convert(DateRange dateRange) {
return Json.createObjectBuilder().add("fromDate",DateFormatData.DATE_FORMAT.format(dateRange.getFrom()))
.add("toDate", DateFormatData.DATE_FORMAT.format(dateRange.getTo()))
.build()
.toString();
}
}
public class StringToDateRangeConverter implements Converter<String,DateRange> {
#Override
public DateRange convert(String dateRange) {
DateRange range = new DateRange();
JsonObject jsonObject = Json.createReader(new StringReader(dateRange)).readObject();
try {
range.setFrom(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("fromDate")));
} catch (ParseException e) {
e.printStackTrace();
}
try {
range.setTo(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("toDate")));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(range);
return range;
}
}
in this way you can listgening on the url: http://localhost:8080/reservation?hotelName=myHotel&reservationDateRange={"fromDate":"14.04.2016","toDate":"15.04.2016"}
pleas pay attenction on reservation DateRange field because I encoded it like a json.
I hope that it can help you
I have a Spring REST web service which populates a generic object based on data we have in a database, the goal is to have the users pass a parameter to the web service to to indicate the format they want the output to be in. Based on their input we will use the correct JSONSerializer to give them what they want.
I have set up my webservice as follows, in my spring-ws-servlet.xml I have set our company ObjectMapper to be used by the mvc:message-converters, I have also set it on the RestController so that it can adjust the ObjectMapper to register the serializer. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="endpoint" class="org.company.Controller">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.company.CompanyObjectMapper" />
</beans>
The controller looks like this:
#RestController
public class Controller {
private ObjectMapper objectMapper;
#RequestMapping(...)
public GenericObject getObject(#PathVariables ...) {
//Get Object from database, just creating an object for example
GenericObject object = new GenericObject();
//Based on the user input we will pick out
//a Serializer that extends JsonSerializer<GenericObject>
BaseSerializer serializer = getSerializer();
//Create a simpleModule and use it to register our serializer
SimpleModule module = new SimpleModule();
module.addSerializer(GenericObject.class, serializer);
//get module and register the serializer
ObjectMapper mapper = getObjectMapper();
mapper.registerModule(module);
return object;
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
The issue is that when I publish my webapp, the first query works correctly, if I specify format=format1, I will get the output in format1. However, after that I can only receive format1. I may specify format=format2, but still get the output in format1. I believe the issue is that the ObjectMapper still has the module registered to it from the first query. I have read that I can avoid this problem by creating a new ObjectMapper every time, but I am not sure how to set that to be used by Spring when it outputs the JSON.
Could someone help me come up with a solution to either create a new ObjectMapper every time I run the code and set that ObjectMapper to the Spring rest service, or help me figure out how I can "unregister" any modules that are registered on the object mapper before setting the latest desired serializer?
An idea could be to create and configure all the mappers you need at startup time as a spring beans.
Then create the default object mapper that will work as a dispatcher for other object mappers (or as the fallback one), and it may be aware of the current http request.
You can register all the mappers in this object mapper, register this mapper to be used as the default one in spring.
Something like this maybe :
public class RequestAwareObjectMapper extends ObjectMapper{
private Map<String, ObjectMapper > mappers = new HashMap<>();
#Override
public String writeValueAsString(Object value) throws JsonProcessingException{
HttpServletRequest req = null;//get request from spring context, if any, this is a managed spring bean it wont be a prorblem
String param = null; // read the param from the query
ObjectMapper mapper = mappers.get(param);
if(mapper == null){
mapper = this;
}
return mapper.writeValueAsString(value);
}
public void registerMapper(String key, ObjectMapper mapper){...}
}
in this way you are not going to pollute your controller with references to the object mapper and you can carry on using #ResponseBody (thanks to #RestController)..
I am sure there's a cleaner way to achieve the same result integrating a similar solution in the spring flow, can't look on something better right now.
Create your customObjectMapper class and auto wire it to your controller using #Autowire annotation. You can then create different methods to create different formatted objects.
You can also send serialiser as parameters.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
super.setSerializationInclusion(JsonInclude.Include.ALWAYS);
super.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
..... etc.....
super.setDateFormat(df);
}
public byte[] generateJsonFormat1(Object value, BaseSerializer serializer) throws IOException, JsonGenerationException, JsonMappingException {
Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION, false);
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
.....
.....
hm.addSerializer(Object.class, serializer);
return super.registerModule(hm).writeValueAsBytes(value);
}
public byte[] generateJsonFormat2(Object value, BaseSerializer serialiser) throws IOException, JsonGe nerationException, JsonMappingException {
SimpleModule sm = new SimpleModule();
sm.addSerializer(Object.class, serialiser);
return super.registerModule(hm).writeValueAsBytes(value);
}
}
Above code is a snippet from my own application. I hope it gives the idea.
I've to manage the association between bean instance and some type of resource. More specifically, when I receive some data associated to Resource A, I have to dispatch the data to the create an instance of some bean type every time I receive data associated with a specified resource. For example, if I receive some data associated with Resource "a", then i have to dispatch this data to a Bean instance associated with "a". If there isn't a Bean instance associated to Resource "a", then it has to be created. The number of Resource isn't statically known, and it will be known only during execution.
I understand I need some type of dictionary to store the association between Resource id and bean instance, building something like a Factory to manage bean creation.
How can I implement this in Spring? What's the proper way?
the Spring ApplicationContext can be referred to in this 'dictionary' fashion. that is, you specify to the application context to 'setup' the 'resource beans'. you then map the 'received data' via way of a Map object, that, in turn, holds a reference to the bean. once the data maps to the bean, you can then retrieve the bean and process the received data. here's a brief example
here's a resource service interface
package de.incompleteco.spring.service;
public interface ResourceService {
public void processData(Object data);
}
here's an implementation to process 'TypeA' data
package de.incompleteco.spring.service;
public class TypeAResourceService implements ResourceService {
public void processData(Object data) {
//do something for 'type A' data
}
}
here's a 'delegate' service
package de.incompleteco.spring.service;
import java.util.Map;
import de.incompleteco.spring.domain.TypeA;
import de.incompleteco.spring.domain.TypeB;
public class DelegateResourceService implements ResourceService {
private Map<String,ResourceService> services;
public void processData(Object data) {
if (data instanceof TypeA) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else if (data instanceof TypeB) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else {
throw new IllegalArgumentException("...");
}
}
public void setServices(Map<String, ResourceService> services) {
this.services = services;
}
}
the 'delegate' service is responsible for retrieving the right service from a map to process the right data (keyed by Class Name)
here's the spring configuration to support
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- type 'a' bean -->
<bean id="typeA" class="de.incompleteco.spring.service.TypeAResourceService"/>
<!-- type 'b' bean -->
<bean id="typeB" class="de.incompleteco.spring.service.TypeBResourceService"/>
<util:map id="dictionary">
<entry key="TypeA" value-ref="typeA"/>
<entry key="TypeB" value-ref="typeB"/>
</util:map>
<bean class="de.incompleteco.spring.service.DelegateResourceService">
<property name="services" ref="dictionary"/>
</bean>
</beans>
this configuration does the following;
sets up services for type A and B data
maps those services to keys
sets up the delegate service, setting the map to be used
this pattern is not the only way of doing it, but gives you the following;
ability to configure the service beans independantly
the delegate only needs a map to process
the constraint is that the service objects have to be of the same interface
you can think of the Spring ApplicationContext as a giant factory, but (in many ways) a whole lot simpler to setup.
I have an application running with Spring, and I'm using AOP in some places. Since I want to use the #Transactional annotation at interface level, I have to allow Spring to create JDK proxies. So, I don't set the proxy-target-class property to true. On the other hand, I don't want to create an interface for every single class I want advised: if the interface just doesn't make sense, I want to have just the implementation, and Spring should create a CGLIB proxy.
Everything was working perfectly, just as I described. But I wanted to have some other annotations (created by me) going in interfaces and being "inherited" by the implementation classes (just like the #Transactional one). Turns out that I can't do that with the built-in support for AOP in Spring (at least I could not figure it out how to do it after some research. The annotation in the interface is not visible in the implementation class, and hence that class does not get advised).
So I decided to implement my own pointcut and interceptor, allowing other method annotations to go on interfaces. Basically, my pointcut look for the annotation on the method and, until not found, in the same method (same name and parameter types) of the interfaces that the class or its superclasses implements.
The problem is: when I declare a DefaultAdvisorAutoProxyCreator bean, that will properly apply this pointcut/interceptor, the behavior of advising classes with no interfaces is broken. Apparently something goes wrong and Spring tries to proxy my classes twice, once with CGLIB and then with JDK.
This is my configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- Activates various annotations to be detected in bean classes: Spring's
#Required and #Autowired, as well as JSR 250's #Resource. -->
<context:annotation-config />
<context:component-scan base-package="mypackage" />
<!-- Instruct Spring to perform declarative transaction management automatically
on annotated classes. -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="logger.advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<constructor-arg>
<bean class="mypackage.MethodAnnotationPointcut">
<constructor-arg value="mypackage.Trace" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="mypackage.TraceInterceptor" />
</constructor-arg>
</bean>
</beans>
This is the class I want to be proxied, with no interfaces:
#Component
public class ServiceExecutorImpl
{
#Transactional
public Object execute(...)
{
...
}
}
When I try to autowire it in some other bean, like:
public class BaseService {
#Autowired
private ServiceExecutorImpl serviceExecutorImpl;
...
}
I get the following exception:
java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26
This are some lines of the Spring output:
13:51:12,672 [main] DEBUG [org.springframework.aop.framework.Cglib2AopProxy] - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [mypackage.ServiceExecutorImpl#1eb515]
...
13:51:12,782 [main] DEBUG [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'serviceExecutorImpl' with 0 common interceptors and 1 specific interceptors
13:51:12,783 [main] DEBUG [org.springframework.aop.framework.JdkDynamicAopProxy] - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [mypackage.ServiceExecutorImpl$$EnhancerByCGLIB$$2eb5f51#5f31b0]
I could supply the full output if someone thinks it will help. I have no idea why Spring is trying to "double-proxy" my class, and why this just happens when I declare the DefaultAdvisorAutoProxyCreator bean.
I have been struggling with this for some time now, and any help or ideas would be very much appreciated.
EDIT:
This is my interceptor source code, as requested. It basically log the method execution (only methods annotated with #Trace get intercepted). If the method is annotated with #Trace(false), the logging is suspended until the method returns.
public class TraceInterceptor
implements
MethodInterceptor
{
#Override
public Object invoke(
MethodInvocation invocation )
throws Throwable
{
if( ThreadExecutionContext.getCurrentContext().isLogSuspended() ) {
return invocation.proceed();
}
Method method = AopUtils.getMostSpecificMethod( invocation.getMethod(),
invocation.getThis().getClass() );
Trace traceAnnotation = method.getAnnotation( Trace.class );
if( traceAnnotation != null && traceAnnotation.value() == false ) {
ThreadExecutionContext.getCurrentContext().suspendLogging();
Object result = invocation.proceed();
ThreadExecutionContext.getCurrentContext().resumeLogging();
return result;
}
ThreadExecutionContext.startNestedLevel();
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd/MM/yyyy - HH:mm:ss.SSS" );
Logger.log( "Timestamp: " + dateFormat.format( new Date() ) );
String toString = invocation.getThis().toString();
Logger.log( "Class: " + toString.substring( 0, toString.lastIndexOf( '#' ) ) );
Logger.log( "Method: " + getMethodName( method ) );
Logger.log( "Parameters: " );
for( Object arg : invocation.getArguments() ) {
Logger.log( arg );
}
long before = System.currentTimeMillis();
try {
Object result = invocation.proceed();
Logger.log( "Return: " );
Logger.log( result );
return result;
} finally {
long after = System.currentTimeMillis();
Logger.log( "Total execution time (ms): " + ( after - before ) );
ThreadExecutionContext.endNestedLevel();
}
}
// Just formats a method name, with parameter and return types
private String getMethodName(
Method method )
{
StringBuffer methodName = new StringBuffer( method.getReturnType().getSimpleName() + " "
+ method.getName() + "(" );
Class<?>[] parameterTypes = method.getParameterTypes();
if( parameterTypes.length == 0 ) {
methodName.append( ")" );
} else {
int index;
for( index = 0; index < ( parameterTypes.length - 1 ); index++ ) {
methodName.append( parameterTypes[ index ].getSimpleName() + ", " );
}
methodName.append( parameterTypes[ index ].getSimpleName() + ")" );
}
return methodName.toString();
}
}
Thanks!
I found a solution using the 'scoped-proxy' suggested by Bozho.
Since I'm using almost only annotations, my ServiceExecutor class now looks like this:
#Component
#Scope( proxyMode = ScopedProxyMode.TARGET_CLASS )
public class ServiceExecutor
{
#Transactional
public Object execute(...)
{
...
}
}
Until now everything seens to be working fine. I don't know why I have to explicitly tell Spring this class should be proxied using CGLIB, since it does not implement any interface. Maybe it's a bug, I don't know.
Thanks a lot, Bozho.
Something doesn't match here - if ther is a $ProxyXX, it means there is an interface. Make sure there is no interface. Some other notes:
in your pointcut you can check if the target object is already a proxy using (x instanceof Advised), then you can cast to Advised
you can use <aop:scoped-proxy /> to define the proxy strategy per-bean