Unable to configure Spring AOP with Gradle - java

I am trying to log exception using SpringBoot and AOP. Using gradlew and Java 1.8.
Main.java
#SpringBootApplication
#EnableAutoConfiguration
#EnableAspectJAutoProxy
public class Main implements CommandLineRunner {
#Override
public void run(String... args) {
try{
ThrowingExample();
}catch(Exception e){
System.out.println("This message is printed");
}
}
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
static void ThrowingMethod() throws FileNotFoundException {
throw new FileNotFoundException();
}
}
AfterThrowAspect.java
#Component("AfterThrowAspect")
#Aspect
public class AfterThrowAspect {
#AfterThrowing(pointcut = "execution(* *.*.*(..))", throwing = "exception")
public void logAfterThrowing(Exception exception) {
System.out.println("Not Printed #AfterReturning:"+new Date());
System.out.println("Exception caught:"+ exception.getMessage());**
}
}
My Gradle File is
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'
mainClassName = 'learn.Main'
repositories {
maven {
url "http://repo1.maven.org/maven2"
}
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
configurations {
aspectjweaver
}
dependencies {
compile "joda-time:joda-time:2.2"
testCompile "junit:junit:4.12"
compile("org.springframework.boot:spring-boot-starter-web") {
exclude module: "spring-boot-starter-tomcat"
}
compile "org.springframework:spring-webmvc:4.+"
compile "org.springframework:spring-aop:4.+"
compile "org.springframework.boot:spring-boot-starter-web:1.+"
compile "org.aspectj:aspectjrt:1.+"
compile "org.slf4j:slf4j-api:1.+"
aspectjweaver "org.aspectj:aspectjweaver:1.+"
runtime configurations.aspectjweaver.dependencies
}
Method logAfterThrowing is never called after exception. I am using Intellj and ide says ThrowingMethod is AdvisedMethod.
I am new to Java. Looking around on web makes me feel this should work but not happening. It compiles and run but logAfterThrowing from AfterThrowAspect.java never called. All files are on same hierarchy and same package.

EDIT
I think I have found the problem with your code. Spring aspect-oriented programming will only work with beans that are maintained by spring container. Since the method throwing the exception is a static method there is no way spring can intercept this.
How to intercept static methods in Spring?
Solution
Define your method in a service or a component and autowire it.
Working example repo https://github.com/mirmdasif/springmvc/tree/master/aopexceptionhandling
Complete Answer
First, define your method in a service bean
#Service
public class ExceptionalService {
public void thorowException() throws Exception {
throw new Exception();
}
}
Second, Instead of #component annotation, you should use #configuration in your AspectJ class. Also, use proper package name in pointcut
#Aspect
#Configuration
public class ErrorInterceptor {
#AfterThrowing(pointcut = "execution(* net.asifhossain.*.*(..))", throwing = "ex")
public void errorInterceptor(Exception ex) {
ex.printStackTrace();
}
}
Third Autowire the service and use it.
#SpringBootApplication
#EnableAspectJAutoProxy
public class AopExampleApp implements CommandLineRunner {
#Autowired
ExceptionalService service;
#Override
public void run(String... args) throws Exception {
try {
service.thorowException();
thorowException();
} catch (Exception ex) {
// Do nothing Since aop will log the answer
}
}
public static void main(String[] args) {
SpringApplication.run(AopExampleApp.class);
}
public static void thorowException() throws Exception {
throw new Exception();
}
}
I have created a blog post with complete step by step procedure of how we can handle exception using Spring's Aspect Oriented Programming.
You can access it at the following link
Handling Exception Using Spring AOP

Related

null pointer exception at service in spring boot using #Autowired [duplicate]

This question already has answers here:
SpringApplication.run main method
(3 answers)
Closed 1 year ago.
I have a service that has a function that prints a string :
#Service
public class Aservice {
public void write(String test) {
System.out.println(test);
}
}
I'm just trying to call this function in the main function, but it gives me a null pointer exception for the service, what am i doing wrong ?
#SpringBootApplication
public class TestApplication {
#Autowired
private Aservice aservice;
public static void main(String[] args) {
TestApplication test = new TestApplication();
test.start();
}
public void start() {
aservice.write("ddd");
}
}
here's the error:
Exception in thread "main" java.lang.NullPointerException
at com.example.test.TestApplication.start(TestApplication.java:19)
at com.example.test.TestApplication.main(TestApplication.java:15)
and here's my build.gradle:
plugins {
id 'org.springframework.boot' version '2.5.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
The correct way to run a Spring Boot application is to use the SpringApplication run method:
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
This will bootstrap the Spring framework, including component scanning, dependency injection, etc.

Sharing an instance of a class across a spring boot application

I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}

How to disable autoconfiguration for undertow

I have a normal spring-boot web application using spring-boot-starter-web, i.e. an embedded tomcat.
Now one of the libraries I'm using for testing comes with undertow as a dependency (because it itself is starting an embedded webserver for mocking an external dependency), and this seems to get the spring-boot autoconfiguration to try to configure undertow as the embedded web server (which seems to break due to version mismatches, and is also not what I want – I'm fine with tomcat as my server).
Here is our test class:
package org.zalando.nakadiproducer.tests;
[... imports skipped ...]
import static io.restassured.RestAssured.given;
#RunWith(SpringRunner.class)
#SpringBootTest(
// This line looks like that by intention: We want to test that the MockNakadiPublishingClient will be picked up
// by our starter *even if* it has been defined *after* the application itself. This has been a problem until
// this commit.
classes = { Application.class, MockNakadiConfig.class },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
//#EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class)
public class ApplicationIT {
#LocalManagementPort
private int localManagementPort;
#ClassRule
public static final EnvironmentVariables environmentVariables
= new EnvironmentVariables();
#BeforeClass
public static void fakeCredentialsDir() {
environmentVariables.set("CREDENTIALS_DIR", new File("src/main/test/tokens").getAbsolutePath());
}
#Test
public void shouldSuccessfullyStartAndSnapshotCanBeTriggered() {
given().baseUri("http://localhost:" + localManagementPort).contentType("application/json")
.when().post("/actuator/snapshot-event-creation/eventtype")
.then().statusCode(204);
}
}
With the main application class:
package org.zalando.nakadiproducer.tests;
[imports skipped]
#EnableAutoConfiguration
#EnableNakadiProducer
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
#Primary
public DataSource dataSource() throws IOException {
return embeddedPostgres().getPostgresDatabase();
}
#Bean
public EmbeddedPostgres embeddedPostgres() throws IOException {
return EmbeddedPostgres.start();
}
#Bean
public SnapshotEventGenerator snapshotEventGenerator() {
return new SimpleSnapshotEventGenerator("eventtype", (withIdGreaterThan, filter) -> {
if (withIdGreaterThan == null) {
return Collections.singletonList(new Snapshot("1", "foo", filter));
} else if (withIdGreaterThan.equals("1")) {
return Collections.singletonList(new Snapshot("2", "foo", filter));
} else {
return new ArrayList<>();
}
});
// Todo: Test that some events arrive at a local nakadi mock
}
}
This is the error message:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'undertowWebServerFactoryCustomizer' defined in class path resource [org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$UndertowWebServerFactoryCustomizerConfiguration.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer] from ClassLoader [sun.misc.Launcher$AppClassLoader#378fd1ac]
The mentioned definition class is in spring-boot-autoconfigure 2.0.3.RELEASE, and looks like this:
#Configuration
#EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
#ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
// tomcat, jetty
/**
* Nested configuration if Undertow is being used.
*/
#Configuration
#ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
public static class UndertowWebServerFactoryCustomizerConfiguration {
#Bean
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
}
How can I tell spring-boot to not configure Undertow?
I tried #EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class) on my test class (beside #SpringBootTest), but that has no effect.
If I try #EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.UndertowWebServerFactoryCustomizerConfiguration.class), I get this error:
Caused by: java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
- org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$UndertowWebServerFactoryCustomizerConfiguration
Removing Undertow from your project's dependencies is the safest way. Spring Boot is based on classpath scanning so once Undertow is gone from the classpath it's auto-configuration won't be processed.
The problem with EmbeddedWebServerFactoryCustomizerAutoConfiguration is that it doesn't provide a property switch. It's purely based on servlet container class presence. To get rid of it, you would have to exclude the entire EmbeddedWebServerFactoryCustomizerAutoConfiguration:
#EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class)
public MyTest {
}
and in your test configuration define just the beans for starting Tomcat:
#TestConfiguraton
#EnableConfigurationProperties(ServerProperties.class)
public MyTestConfig {
#Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}

Aspectj not working on simple example

I think i am missing something basic, i can't get aspectJ to work.
(i am new to aspectj)
here is my aspect file
#Aspect
public class AspectAPILogger {
#Pointcut("execution(* *(..))")
public void atExecution(){
System.out.println("AspectAPILogger.atExecution------------");
}
#Around("atExecution()")
public void aroundX1(JoinPoint joinPoint) {
System.out.println("AspectAPILogger.aroundX1------------");
}
#Before("execution(* *(..))")
public void beforeX(JoinPoint joinPoint) {
System.out.println("AspectAPILogger.beforeX------------");
}
}
my gradle.build
repositories {
mavenCentral()
}
//https://plugins.gradle.org/plugin/aspectj.gradle
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.aspectj:gradle-aspectj:0.1.6"
}
}
apply plugin: "aspectj.gradle"
plugins {
id "aspectj.gradle" version "0.1.6"
}
....
ext.versionAspectJ = '1.8.10';
...
dependencies {
.....
//AOP
compile group: 'org.aspectj' , name: 'aspectjrt' , version: "$versionAspectJ"
compile group: 'org.aspectj' , name: 'aspectjweaver' , version: "$versionAspectJ"
.....
}
some small main
public class TestMain {
public static void main(String[] args) {
Utils.md5("---");
Utils.timerStart();
Utils.timerStop("Some log message ");
}
}
can you help me figure out what i am missing, i guess its something basic... i also tries this plugin 'https://github.com/NikitaKozlov/WeaverLite' but i can't get a simple aspect to work, please help...
my project is a simple gradle java project
thank you !!!

Spring dependency injection into Spring TestExecutionListeners not working

How can I use Spring dependency injection into a TestExecutionListener class I wrote extending AbstractTestExecutionListener?
Spring DI does not seem to work with TestExecutionListener classes.
Example of issue:
The AbstractTestExecutionListener:
class SimpleClassTestListener extends AbstractTestExecutionListener {
#Autowired
protected String simplefield; // does not work simplefield = null
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
System.out.println("simplefield " + simplefield);
}
}
Configuration file:
#Configuration
#ComponentScan(basePackages = { "com.example*" })
class SimpleConfig {
#Bean
public String simpleField() {
return "simpleField";
}
}
The JUnit Test file:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SimpleConfig.class })
#TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS, listeners = {
SimpleClassTestListener.class })
public class SimpleTest {
#Test
public void test(){
assertTrue();
}
}
As highlighted in the code comment, when I run this, it will print "simplefield null" because simplefield never gets injected with a value.
Just add autowiring for the whole TestExecutionListener.
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this);
// your code that uses autowired fields
}
Check sample project in github.
In the case of Spring Boot 2 using
estContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this)
was triggering the creation of the Spring context before the #SpringBootTest base class was created. This missed then some critical configuration parameters in my case. I had to use testContext.getApplicationContext().getBean( in beforeTestClass for getting a bean instance.

Categories