Merge pull request #11638 from freelansam/JAVA-8280

JAVA-8280: Split or move spring-aop module
This commit is contained in:
kwoyke
2022-01-04 09:53:31 +01:00
committed by GitHub
26 changed files with 109 additions and 7 deletions

View File

@@ -6,11 +6,9 @@ This module contains articles about Spring aspect oriented programming (AOP)
- [Implementing a Custom Spring AOP Annotation](https://www.baeldung.com/spring-aop-annotation)
- [Intro to AspectJ](https://www.baeldung.com/aspectj)
- [Spring Performance Logging](https://www.baeldung.com/spring-performance-logging)
- [Introduction to Spring AOP](https://www.baeldung.com/spring-aop)
- [Introduction to Pointcut Expressions in Spring](https://www.baeldung.com/spring-aop-pointcut-tutorial)
- [Introduction to Advice Types in Spring](https://www.baeldung.com/spring-aop-advice-tutorial)
- [When Does Java Throw UndeclaredThrowableException?](https://www.baeldung.com/java-undeclaredthrowableexception)
- [Get Advised Method Info in Spring AOP](https://www.baeldung.com/spring-aop-get-advised-method-info)
- [Advise Methods on Annotated Classes With AspectJ](https://www.baeldung.com/aspectj-advise-methods)
- [Joinpoint vs. ProceedingJoinPoint in AspectJ](https://www.baeldung.com/aspectj-joinpoint-proceedingjoinpoint)
- More articles: [[next -->]](/spring-aop-2)

View File

@@ -1,28 +0,0 @@
package com.baeldung.method.info;
public class Account {
private String accountNumber;
private double balance;
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" + "accountNumber='" + accountNumber + '\'' + ", balance=" + balance + '}';
}
}

View File

@@ -1,12 +0,0 @@
package com.baeldung.method.info;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccountOperation {
String operation();
}

View File

@@ -1,55 +0,0 @@
package com.baeldung.method.info;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@Aspect
@Component
public class BankAccountAspect {
@Before(value = "@annotation(com.baeldung.method.info.AccountOperation)")
public void getAccountOperationInfo(JoinPoint joinPoint) {
// Method Information
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
System.out.println("full method description: " + signature.getMethod());
System.out.println("method name: " + signature.getMethod().getName());
System.out.println("declaring type: " + signature.getDeclaringType());
// Method args
System.out.println("Method args names:");
Arrays.stream(signature.getParameterNames())
.forEach(s -> System.out.println("arg name: " + s));
System.out.println("Method args types:");
Arrays.stream(signature.getParameterTypes())
.forEach(s -> System.out.println("arg type: " + s));
System.out.println("Method args values:");
Arrays.stream(joinPoint.getArgs())
.forEach(o -> System.out.println("arg value: " + o.toString()));
// Additional Information
System.out.println("returning type: " + signature.getReturnType());
System.out.println("method modifier: " + Modifier.toString(signature.getModifiers()));
Arrays.stream(signature.getExceptionTypes())
.forEach(aClass -> System.out.println("exception type: " + aClass));
// Method annotation
Method method = signature.getMethod();
AccountOperation accountOperation = method.getAnnotation(AccountOperation.class);
System.out.println("Account operation annotation: " + accountOperation);
System.out.println("Account operation value: " + accountOperation.operation());
}
}

View File

@@ -1,29 +0,0 @@
package com.baeldung.method.info;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;
@Component
public class BankAccountService {
@AccountOperation(operation = "deposit")
public void deposit(Account account, Double amount) {
account.setBalance(account.getBalance() + amount);
}
@AccountOperation(operation = "withdraw")
public void withdraw(Account account, Double amount) throws WithdrawLimitException {
if (amount > 500.0) {
throw new WithdrawLimitException("Withdraw limit exceeded.");
}
account.setBalance(account.getBalance() - amount);
}
public double getBalance() {
return RandomUtils.nextDouble();
}
}

View File

@@ -1,7 +0,0 @@
package com.baeldung.method.info;
public class WithdrawLimitException extends RuntimeException {
public WithdrawLimitException(String message) {
super(message);
}
}

View File

@@ -1,59 +0,0 @@
package com.baeldung.performancemonitor;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.interceptor.PerformanceMonitorInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.time.LocalDate;
import java.time.Month;
@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
@Pointcut("execution(public String com.baeldung.performancemonitor.PersonService.getFullName(..))")
public void monitor() { }
@Pointcut("execution(public int com.baeldung.performancemonitor.PersonService.getAge(..))")
public void myMonitor() { }
@Bean
public PerformanceMonitorInterceptor performanceMonitorInterceptor() {
return new PerformanceMonitorInterceptor(true);
}
@Bean
public Advisor performanceMonitorAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("com.baeldung.performancemonitor.AopConfiguration.monitor()");
return new DefaultPointcutAdvisor(pointcut, performanceMonitorInterceptor());
}
@Bean
public Person person(){
return new Person("John","Smith", LocalDate.of(1980, Month.JANUARY, 12));
}
@Bean
public PersonService personService(){
return new PersonService();
}
@Bean
public MyPerformanceMonitorInterceptor myPerformanceMonitorInterceptor() {
return new MyPerformanceMonitorInterceptor(true);
}
@Bean
public Advisor myPerformanceMonitorAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("com.baeldung.performancemonitor.AopConfiguration.myMonitor()");
return new DefaultPointcutAdvisor(pointcut, myPerformanceMonitorInterceptor());
}
}

View File

@@ -1,39 +0,0 @@
package com.baeldung.performancemonitor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.springframework.aop.interceptor.AbstractMonitoringInterceptor;
import java.util.Date;
public class MyPerformanceMonitorInterceptor extends AbstractMonitoringInterceptor {
public MyPerformanceMonitorInterceptor() {
}
public MyPerformanceMonitorInterceptor(boolean useDynamicLogger) {
setUseDynamicLogger(useDynamicLogger);
}
@Override
protected Object invokeUnderTrace(MethodInvocation invocation, Log log) throws Throwable {
String name = createInvocationTraceName(invocation);
long start = System.currentTimeMillis();
log.info("Method "+name+" execution started at:"+new Date());
try {
return invocation.proceed();
}
finally {
long end = System.currentTimeMillis();
long time = end - start;
log.info("Method "+name+" execution lasted:"+time+" ms");
log.info("Method "+name+" execution ended at:"+new Date());
if (time > 10){
log.warn("Method execution longer than 10 ms!");
}
}
}
}

View File

@@ -1,16 +0,0 @@
package com.baeldung.performancemonitor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class PerfomanceApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfiguration.class);
Person person = (Person) context.getBean("person");
PersonService personService = (PersonService) context.getBean("personService");
System.out.println("Name is:"+personService.getFullName(person));
System.out.println("Age is:"+personService.getAge(person));
}
}

View File

@@ -1,42 +0,0 @@
package com.baeldung.performancemonitor;
import java.time.LocalDate;
public class Person {
private String lastName;
private String firstName;
private LocalDate dateOfBirth;
public Person() {
}
public Person(String firstName, String lastName, LocalDate dateOfBirth) {
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}

View File

@@ -1,17 +0,0 @@
package com.baeldung.performancemonitor;
import java.time.LocalDate;
import java.time.Period;
public class PersonService {
public String getFullName(Person person){
return person.getLastName()+" "+person.getFirstName();
}
public int getAge(Person person){
Period p = Period.between(person.getDateOfBirth(), LocalDate.now());
return p.getYears();
}
}

View File

@@ -13,10 +13,6 @@
<!-- in order to debug some marshalling issues, this needs to be TRACE -->
<logger name="org.springframework.web.servlet.mvc" level="WARN" />
<logger name="com.baeldung.performancemonitor.MyPerformanceMonitorInterceptor" level="INFO" />
<logger name="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" level="TRACE" />
<logger name="com.baeldung.aspectj.classmethodadvice" level="TRACE" />
<root level="TRACE">

View File

@@ -1,49 +0,0 @@
package com.baeldung.method.info;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
class BankAccountServiceIntegrationTest {
private Account account;
@BeforeEach
public void setup() {
account = new Account();
account.setAccountNumber("12345");
account.setBalance(2000.0);
}
@Autowired
BankAccountService bankAccountService;
@Test
void withdraw() {
bankAccountService.withdraw(account, 500.0);
assertTrue(account.getBalance() == 1500.0);
}
@Test
void withdrawWhenLimitReached() {
Assertions.assertThatExceptionOfType(WithdrawLimitException.class)
.isThrownBy(() -> bankAccountService.withdraw(account, 600.0));
assertTrue(account.getBalance() == 2000.0);
}
@Test
void deposit() {
bankAccountService.deposit(account, 500.0);
assertTrue(account.getBalance() == 2500.0);
}
@Test
void getBalance() {
bankAccountService.getBalance();
}
}

View File

@@ -1,7 +0,0 @@
package com.baeldung.undeclared;
public class SomeCheckedException extends Exception {
public SomeCheckedException(String message) {
super(message);
}
}

View File

@@ -1,11 +0,0 @@
package com.baeldung.undeclared;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ThrowUndeclared {
}

View File

@@ -1,12 +0,0 @@
package com.baeldung.undeclared;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UndeclaredApplication {
public static void main(String[] args) {
SpringApplication.run(UndeclaredApplication.class, args);
}
}

View File

@@ -1,16 +0,0 @@
package com.baeldung.undeclared;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UndeclaredAspect {
@Around("@annotation(undeclared)")
public Object advise(ProceedingJoinPoint pjp, ThrowUndeclared undeclared) throws Throwable {
throw new SomeCheckedException("AOP Checked Exception");
}
}

View File

@@ -1,10 +0,0 @@
package com.baeldung.undeclared;
import org.springframework.stereotype.Service;
@Service
public class UndeclaredService {
@ThrowUndeclared
public void doSomething() {}
}

View File

@@ -1,25 +0,0 @@
package com.baeldung.undeclared;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.lang.reflect.UndeclaredThrowableException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UndeclaredApplication.class)
public class UndeclaredThrowableExceptionIntegrationTest {
@Autowired private UndeclaredService service;
@Test
public void givenAnAspect_whenCallingAdvisedMethod_thenShouldWrapTheException() {
assertThatThrownBy(service::doSomething)
.isInstanceOf(UndeclaredThrowableException.class)
.hasCauseInstanceOf(SomeCheckedException.class);
}
}

View File

@@ -1,48 +0,0 @@
package com.baeldung.undeclared;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class UndeclaredThrowableExceptionUnitTest {
@Test
@SuppressWarnings("unchecked")
public void givenAProxy_whenProxyUndeclaredThrowsCheckedException_thenShouldBeWrapped() {
ClassLoader classLoader = getClass().getClassLoader();
InvocationHandler invocationHandler = new ExceptionalInvocationHandler();
List<String> proxy = (List<String>) Proxy.newProxyInstance(classLoader, new Class[] { List.class }, invocationHandler);
assertThatThrownBy(proxy::size)
.isInstanceOf(UndeclaredThrowableException.class)
.hasCauseInstanceOf(SomeCheckedException.class);
}
@Test
@SuppressWarnings("unchecked")
public void givenAProxy_whenProxyThrowsUncheckedException_thenShouldBeThrownAsIs() {
ClassLoader classLoader = getClass().getClassLoader();
InvocationHandler invocationHandler = new ExceptionalInvocationHandler();
List<String> proxy = (List<String>) Proxy.newProxyInstance(classLoader, new Class[] { List.class }, invocationHandler);
assertThatThrownBy(proxy::isEmpty).isInstanceOf(RuntimeException.class);
}
private static class ExceptionalInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("size".equals(method.getName())) {
throw new SomeCheckedException("Always fails");
}
throw new RuntimeException();
}
}
}