I am trying to get Aspect working with Spring 3 and annotations.
#Aspect
public class AttributeAspect {
#Pointcut("#annotation(com.mak.selective.annotation.Attribute)")
public void process(){
System.out.println("Inside Process ....");
}
#Around("process()")
public void processAttribute(){
System.out.println("Inside Actual Aspect ..");
}
}
XML:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="false" />
<context:component-scan base-package="com.mak.selective.annotation.*" />
<bean name="attribute" class="com.mak.selective.annotation.AttributeAspect"/>
</beans>
MY Test to test the Aspect:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/springcontext/*.xml")
public class AttributeTest {
#Attribute(tableName = "firstTable", columnName = "New Column")
private void getAttribute() {
System.out.println("Inside Attribute call...");
}
#Test
public void testAttributeAspect() {
getAttribute();
}
}
With this code i can only see "Inside Attribute call..." but nothing from Aspect.
Please guide.
Got this working by making a new Object (Component) and injected to the Junit test class.
Good to see that you got it working from XML, but you could have also done it from annotations.
The issue is that the #Aspect annotation is not a Spring stereotype, so the scanner is not registering the aspect as a Spring Bean. Just add either #Service or #Component above or below #Aspect and it will be registered.
Also, either directly name the bean (e.g., #Service("myNamedService")) or have it implement an interface (e.g., public class AttributeAspect implements IAspect {), as per standard Spring design.
You need to use real AspectJ if you want to intercept invocations of methods within the same bean form where it is invoked. (What you have done, should work if the method testAttributeAspect() is located in an other bean.)
How to do real AspectJ?
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is discussed in Section 7.8, “Using AspectJ with Spring applications”.
#See Spring Reference
A few things:
Firstly, when you do around advice you need to write the advice method like this:
#Around(...)
public void aroundAdviceMethod(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("before...");
pjp.proceed();
}
finally {
System.out.println("After...");
}
}
But also (and this at least applies when you're using proxies, not entirely sure in your case), the method you're putting advice on needs to be public (yours isn't), spring managed (via #Component or otherwise) and called external from the class so the proxy can take effect (also not the case in your example). So you need something like this:
#Component
public class SomeClass {
#Attribute
public void someMethodCall() {
System.out.println("In method call");
}
}
public class SomeUnitTest {
#Autowired SomeClass someClass;
#Test
public void testAspect() {
someClass.someMethodCall();
}
}
Related
I am trying to wire beans using Annotations.. When a beans is not present in the beans.xml config file, i am getting a null pointer exception.. i expect the required=false attribute to fix this.. is that a fair expectation? If so, why is it still throwing exception even if i set required as false for that missing bean...
package com.rajkumar.spring;
import org.springframework.beans.factory.annotation.Autowired;
public class Log {
private ConsoleWriter consoleWriter;
private FileWriter fileWriter;
#Autowired
public void setConsoleWriter(ConsoleWriter consoleWriter) {
this.consoleWriter = consoleWriter;
}
#Autowired(required=false)
public void setFileWriter(FileWriter fileWriter) {
this.fileWriter = fileWriter;
}
public void writeToFile(String message) {
fileWriter.write(message); // this is throwing the error as the bean is comments in the XML file..
}
public void writeToConsole(String message) {
consoleWriter.write(message);
}
}
My Beans.xml is below..
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="log" class="com.rajkumar.spring.Log"></bean>
<bean id="consoleWriter"
class="com.rajkumar.spring.ConsoleWriter">
</bean>
<!-- <bean id="fileWriter" class="com.rajkumar.spring.FileWriter"></bean> -->
<context:annotation-config></context:annotation-config>
</beans>
required=false is to only make spring container dependency checking optional and it avoids correpsonding bean not found exception like.. required a bean of type 'com.rajkumar.spring.FileWriter' that could not be found.
Please note that injected object is still null here, hence you are seeing NullPointerException.
Hope this helps you.
Try to add the #Component Annotation above your class definition.
#Component
public class Log {
...
Without Spring won't recognize your class to inject something somewhere and your fields will remain null. You might also want to add ComponentScan to your configuration.
public void writeToFile(String message) {
fileWriter.write(message); // this is throwing the error as the bean is comments in the XML file..
}
The error is throwing because there is no bean injected to fileWriter if the bean will not be injected then try to validate if the object is not null before use it.
public void writeToFile(String message) {
if (fileWriter!=null)
fileWriter.write(message); // this is throwing the error as the bean is comments in the XML file..
}
Another approach is to use a #PostConstruct, for example:
#PostConstruct
public void initBean(){
if (fileWriter ==null) fileWriter = new SomeFileWriter();
}
In that case there is no need to evaluate fileWriter on writeToFile method
Also you can use a init method instead of #PostConstruct
Like this:
<bean id="log" class="com.rajkumar.spring.Log" init-method="myInit"></bean>
then on myInit() method try to initialize your null object.
I am trying to log a method using AOP in Spring. I tried the following code with just System.out.println() but it's not getting called.
Created annotation:
#Retention(value = RetentionPolicy.RUNTIME)
#Target(value = ElementType.METHOD)
public #interface Loggable {
}
Created Aspect
#Aspect
public class MethodLogger {
#Around("execution(* *(..)) && #annotation(Loggable)")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("this is called");
return result;
}
}
Used logging in a method in service layer
#Service("RegionService")
#Transactional(readOnly = false)
public class RegionService implements IRegionService{
#Loggable
#Override
public List<> fetch() {
return dao.xyz();
}
}
Spring configuration
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"
>
<context:component-scan base-package="com.sst.tender.spring"/>
<context:annotation-config/>
<context:spring-configured/>
</beans>
Add #Component to MethodLogger class. Also you have to enable AspectJ like one of the following ways:
Adding #EnableAspectJAutoProxy to your configuration bean class. (annotation driven approach)
Adding <aop:aspectj-autoproxy /> to application context file. (XML driven approach)
My Aspect class will be ,
#Configuration
#EnableAspectJAutoProxy
#Component
#Aspect
public class AspectClass {
#Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before running the beforeAspect() in the AopTest.java class!");
System.out.println("Hijacked Method name : " + joinPoint.getSignature().getName());
System.out.println("************************");
}
}
My other java Class
public class AopTest {
public void beforeAspect() {
System.out.println("This is beforeAspect() !");
}
}
My Main Class is
public class MainMethod {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("ApplicationContext/applicationContext.xml");
AopTest test = (AopTest)context.getBean("bean1");
test.beforeAspect();
}
}
My applicationContext.xml is ,
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="bean1" class="com.pointel.aop.test1.AopTest" />
</beans>
In this the #Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())") in the AspectClass will not be executed before the beforeAspect() in the AopTest , when running Main method.
Good answers are definitely appreciated.
First of all if you're going to use an annotation based configuration, use AnnotationConfigApplicationContext instead of FileSystemXmlApplicationContext. And get rid of the applicationContext.xml file and simply add a #Bean method in your configuration class. Something like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = "your.aspect.package")
public class AspectConfig {
#Bean
public AopTest aopTest() {
return new AopTest();
}
}
In your main
public class MainMethod {
public static void main(String[] args) {
AnnotationConfigApplicationContextcontext = new AnnotationConfigApplicationContext(AspectConfig.class);
// don't forget to refresh
context.refresh();
AopTest test = (AopTest)context.getBean("aopTest");
test.beforeAspect();
}
}
In AspectClass you should have #Component, #Aspect, and your method should have the advice or pointcut annotation like #Before. It needs to be a #Component, so that Spring knows to scan it.
Here some code need to add in xml to use annotations-
1.for #component annotation.
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
2.after that use component scan to get all annotated bean class which use #component annotation,and use aop autoproxy-
<context:annotation-config/>
<context:component-scan base-package="mypackage"></context:component-scan>
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
for examples visit-www.technicaltoday.com/p/spring.html
You are missing the point cut definition in your aspect class.
For example;
#Pointcut("execution(* *.advice(..))")
public void logBefore(){}
#Before("logBefore()")
public void beforeAdvicing(){
System.out.println("Listen Up!!!!");
}
You first have to defin the point to weave your aspect to. You do this by using Point cuts.It is the point cut name you give within your #Before annotation. Have a look at my blog post for more information # http://dinukaroshan.blogspot.com/2010/06/aop-with-spring.html
I don't see your AspectClass in the beans configuration. You should also declare it as a Bean.
I'm writing my own scope (i.e. a class which implements org.springframework.beans.factory.config.Scope) and I need some beans injected. How do I do that?
Background: Spring must create all scope beans first so that I can define which beans go into the scope. But what about beans that I need to build the scope in the first place?
I came up with this workaround which seems to be pretty safe but I'd like to hear comments (or maybe my answer gives you some better ideas):
Define the scope and give it setters (instead of using #Autowired)
Create a "scope configurer" bean:
public CustomScopeConfigurer {
#Autowired private Foo foo;
private CustomScope scope;
public CustomScopeConfigurer( CustomScope scope ) {
this.scope = scope;
}
#PostConstruct
public void initScope() {
scope.setFoo( foo );
}
}
This configurer bean must not be lazy.
Reasoning:
The scope itself can't use autowiring because it is created before the first bean. While it might be created later, you can be sure that it will be created before every other bean. So autowiring can't work reliably.
The configurer bean will be created alongside all the other beans but after the scope. So autowiring will work for it.
Since the configurer bean isn't initialized lazy, it will be created before the rest of the application can see the application context. This means that no beans for the scope (i.e. beans with #Scope("custom")) can have been created at this time - the scope can't be "active", yet -> Spring won't have tried to put any beans into it, yet.
The scope itself is usually created as a static constant somewhere. That's why we have to pass it as an argument to the constructor.
You can do that very simply.
Consider the below Custom scope class:
package com.way2learn;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class MyCustomScope implements Scope{
private Map<String, Object> scope;
public void setScope(Map<String, Object> scope) {
this.scope = scope;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
checkAndClear();
Object bean=scope.get(name);
if(bean==null){
bean=objectFactory.getObject();
scope.put(name,bean);
}
return bean;
}
private void checkAndClear() {
//Some logic to check condition and clear the scope
}
//optional methods
#Override
public Object remove(String name) {
// TODO Auto-generated method stub
return null;
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
// TODO Auto-generated method stub
return null;
}
#Override
public String getConversationId() {
// TODO Auto-generated method stub
return null;
}
}
It has dependency on java.util.Map.
You can not autowire using #Autowired it as #Autowired annotation works after AutoWiredAnnotationBeanPostProcessor only.
But Custom scopes will be registered before AutoWiredAnnotationBeanPostProcessor.
So you can manually inject Map into MyCustomScope class as below:
<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"
xmlns:context="http://www.springframework.org/schema/context"
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-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<util:map key-type="java.lang.String" value-type="java.lang.Object" id="custScopeMap"/>
<bean id="myCustomScope" class="com.way2learn.MyCustomScope">
<property name="scope" ref="custScopeMap"/>
</bean>
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="myScope" value-ref="myCustomScope"/>
</map>
</property>
</bean>
</beans>
I tried it. It works fine. And I found a bug in Aaron Digulla's answer i.e.
Consider the below scenario: First Spring's CustomScopeConfigurer bean will be created, immediately CustomScope bean will be created and now our custom scope is ready to use. After some time Aaron Digulla's CustomScopeConfigurer will be created which initializes foo into CustomScope. But what happenes if some custom scoped beans are created after CustomScope registration and before Aaron Digulla's CustomScopeConfigurer bean creation? For those beans there will be not foo in the CustomScope bean.
Is it possible to have the equivalent of <mvc:default-servlet-handler/> defined in an AnnotationConfig(Web)ApplicationContext? Right now I have:
#Configuration
#ImportResource("classpath:/mvc-resources.xml")
class AppConfig {
// Other configuration...
}
with just the following in my resources/mvc-resources.xml:
<?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-3.0.xsd">
<mvc:default-servlet-handler/>
</beans>
And it works as expected. Is it possible to do this without importing an XML file? It would be a nice way to cut down on some boilerplate.
If you are using Spring 3.1 with WebMvc, you can configure default servlet handling like this:
#Configuration
#EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
After digging a bit deeper, I found out that this is a known problem and is addressed by annotation features in the upcoming Spring 3.1.
I solved my problem with the following code:
#Configuration
#Import(FeatureConfig.class)
class AppConfig {
...
}
#FeatureConfiguration
class FeatureConfig {
#Feature
public MvcDefaultServletHandler defaultHandler() {
return new MvcDefaultServletHandler();
}
}
This does require using the milestone version of spring, though, but it seems to be the cleanest and preferred way of handling this.
I don't think you can do it out of the box, but you can probably copy what DefaultServletHandlerBeanDefinitionParser does: Create a Bean of type DefaultServletHttpRequestHandler and map it to the URL scheme /**.
I'd say your Bean should subclass DefaultServletHttpRequestHandler and do the mapping in a #PostConstruct method.
#Bean
public DefaultServletHttpRequestHandler defaultServletHttpRequestHandler() {
return new DefaultServletHttpRequestHandler();
}
#Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
Map<String, String> urlMap = new ManagedMap<String, String>();
urlMap.put("/**", defaultServletHandlerName);
SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
hm.setUrlMap(urlMap);
return hm;
}