Verhalten von einem Spring-Controller bei Aspekten

ck2003

Grünschnabel
Ich hab mich gestern mal bisschen mit AspectJ beschäftigt und irgendwie schein ich noch nicht so ganz dahinter zu kommen.

Mal ein einfaches Beispiel. Nehmen wir an, ich hab folgenden Controller

Code:
@Controller
public class TestControllerImpl {

	@RequestMapping(value="/test.do")
	@TestAnnotation
	public String test(TestForm form, HttpServletRequest request) {
		return "test";
	}

}

und den Abschnitt in meinem Aspect

Code:
	@Before(value = "testAnnotation() && args(form, request)")
	public void beforeValidation(Object form, HttpServletRequest request) {
		doSomething(form, request);
	}

        @Pointcut("@annotation(annotation.TestAnnotation)")
	public void testAnnotation() {}

dann klappt das soweit.
Jetzt implementier ich mal das Interface im TestControllerImpl

Code:
public interface TestController {

	public String test(TestForm form, HttpServletRequest request);
}

Bei der Einstellung Standard-Einstellung proxy-target-class="false" kommt
Code:
DEBUG - Creating shared instance of singleton bean 'testControllerImpl'
DEBUG - Creating instance of bean 'testControllerImpl'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Eagerly caching bean 'testControllerImpl' to allow for resolving potential circular references
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Creating implicit proxy for bean 'testControllerImpl' with 0 common interceptors and 2 specific interceptors
DEBUG - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [de.pixelcasino.frontend.controller.TestControllerImpl@1890909]
DEBUG - Finished creating instance of bean 'testControllerImpl'
und das mapping auf "test.do" klappt nichtmehr. Bei der proxy-target-class="true" ändert sich der output in
Code:
DEBUG - Creating shared instance of singleton bean 'testControllerImpl'
DEBUG - Creating instance of bean 'testControllerImpl'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Eagerly caching bean 'testControllerImpl' to allow for resolving potential circular references
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#0'
DEBUG - Returning cached instance of singleton bean 'org.springframework.flex.core.EndpointServiceMessagePointcutAdvisor#1'
DEBUG - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG - Creating implicit proxy for bean 'testControllerImpl' with 0 common interceptors and 2 specific interceptors
DEBUG - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [de.pixelcasino.frontend.controller.TestControllerImpl@1d1f4fe]
DEBUG - Unable to apply any optimisations to advised method: public java.lang.String de.pixelcasino.frontend.controller.TestControllerImpl.test(javax.servlet.http.HttpSession,javax.servlet.http.HttpServletRequest)
DEBUG - Found finalize() method - using NO_OVERRIDE
DEBUG - Found 'hashCode' method: public native int java.lang.Object.hashCode()
DEBUG - Unable to apply any optimisations to advised method: protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
DEBUG - Found 'equals' method: public boolean java.lang.Object.equals(java.lang.Object)
DEBUG - Unable to apply any optimisations to advised method: public java.lang.String java.lang.Object.toString()
DEBUG - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.aopalliance.aop.Advice)
DEBUG - Method is declared on Advised interface: public abstract int org.springframework.aop.framework.Advised.indexOf(org.springframework.aop.Advisor)
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isFrozen()
DEBUG - Method is declared on Advised interface: public abstract org.springframework.aop.TargetSource org.springframework.aop.framework.Advised.getTargetSource()
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvisor(int,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setTargetSource(org.springframework.aop.TargetSource)
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setPreFiltered(boolean)
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isProxyTargetClass()
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.setExposeProxy(boolean)
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isExposeProxy()
DEBUG - Method is declared on Advised interface: public abstract org.springframework.aop.Advisor[] org.springframework.aop.framework.Advised.getAdvisors()
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(int,org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.addAdvice(org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isPreFiltered()
DEBUG - Method is declared on Advised interface: public abstract java.lang.Class[] org.springframework.aop.framework.Advised.getProxiedInterfaces()
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.isInterfaceProxied(java.lang.Class)
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvisor(org.springframework.aop.Advisor)
DEBUG - Method is declared on Advised interface: public abstract void org.springframework.aop.framework.Advised.removeAdvisor(int) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.replaceAdvisor(org.springframework.aop.Advisor,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException
DEBUG - Method is declared on Advised interface: public abstract boolean org.springframework.aop.framework.Advised.removeAdvice(org.aopalliance.aop.Advice)
DEBUG - Method is declared on Advised interface: public abstract java.lang.String org.springframework.aop.framework.Advised.toProxyConfigString()
DEBUG - Method is declared on Advised interface: public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()
DEBUG - Finished creating instance of bean 'testControllerImpl'
und das ganze scheint zu klappen. Was mache ich falsch, dass das bei der Standardeinstellung nicht klappt.
 
Zuletzt bearbeitet:
SpringAOP funktioniert im Defaultfall nur gegen Interfaces (Details in 6.1.3 hier. D.h. deine TestAnnotation müsste an das Interface um deinen Pointcut zu matchen. Durch proxy-target-class="true" kommt CGLib ins Spiel und es werden zur Laufzeit Subklassen deiner Beans erzeugt anstelle von wrappenden JDK Proxies. Dadurch gelangt die Annotation wieder in den Scope und der Pointcut matcht.

Gruß
Ollie
 
Ok, dass die Annotation dann auch rüber muss war mir nicht klar, aber das wäre ja schnell gemacht.

Davon abgesehen kommt er aber sowieso nie zu dem Punkt, weil das mapping auf "/test.do" gar nichtmehr erkannt wird.
Code:
	DEBUG - Rejected bean name 'testControllerImpl': no URL paths identified
 
Erst hatte ich es so.

Code:
@Controller
public class TestControllerImpl implements TestController {

	@RequestMapping(value="/test.do")
	public String test(TestForm form, HttpServletRequest request) {
		return "test";
	}

}

public interface TestController {

	@TestAnnotation
	public String test(TestForm form, HttpServletRequest request);
}

Dann findet er im DefaultAnnotationHandlerMapping aber das @Controller nichtmehr als Annotation, weswegen er gar nicht erst versucht die URLs zu extrahieren. Wenn ich mal testweise das @Controller zusätzlich ans Interface setze, was wohl nicht im Sinne Erfinders ist, findet er das zwar dann, aber spätestens beim Extrahieren von @RequestMapping gibt er auf.
 
Sicher? Hast du die Logausgaben für die Konfiguration mal da? Hab grad mal im Code geschaut und seh keinen Grund warum das nicht tun sollte...
 
Was sollte gehen? Wenn @Controller und @RequestMapping in TestControllerImpl liegen? Hab ich da was falsch konfiguriert? An den Annotation in TestControllerImpl selbst kommt er bei mir nie vorbei.

Außer
Code:
DEBUG - Rejected bean name 'testControllerImpl': no URL paths identified
loggt der bei mir in dem Bereich zu dem Fall nix oder könnte davor oder dahinter noch was spannendes für dich auftauchen.
 
Ich meine, dass es so funktionieren sollte, wie du es da hast... Die kritische Methode ist wohl DefaultAnnotationHandlerMapping.determineUrlsForHandler(String beanName). Da wirft er deine Bean weg, obwohl er das nicht sollte. Da solltest du mal den Debugger drauf ansetzen und schauen, was da drin genau passiert.
 
Mal am Beispiel von
Code:
AnnotationUtils.findAnnotation(handlerType, Controller.class)
was im unteren Bereich der angesprochenen Methode zu finden ist.

Zuerst schaut er im Proxy selbst: der hat die Annotation nicht.

Dann geht er die Interfaces durch.:
TestController
org.springframework.aop.SpringProxy
org.springframework.aop.framework.Advised

als nächstes geht er auf die Superklasse
java.lang.reflect.Proxy

die hat nur
Serializable

und dann kommt noch java.lang.Object dran

wer von denen sollte denn nun die Annotationen von TestControllerImpl vorhalten?
 
Args, daran hab ich nicht gedacht... wenn die Klasse geproxied wird, dann hat sie natürlich nicht mehr den Typ der Implemenierung. Damit sind auch die Annotationen nicht mehr sichtbar. Hast du mal geschaut, ob es a) nen Bug dazu im JIRA gibt? und/oder b) es im 3.0 Milestone evtl. schon gefixt ist?

EDIT: Hab grad kurz mit Eberhard Wolff gesprochen. Scheint tatsächlich ein Bug zu sein. Ich konnte auf die Schnelle im JIRA nix finden. Ich file da morgen früh mal was...
 

Neue Beiträge

Zurück