What is Spring Aspect Oriented Programming?

I have to admit that AOP and all the concepts behind it are not easy to catch at the beginning. After reading some definitions about Spring-aop and looking examples, I found a good and simple definition that I’d like to share:

“Spring AOP (Aspect-oriented programming) framework is used to modularize cross-cutting concerns in aspects. Put it simple, it’s just an interceptor to intercept some processes, for example, when a method is executed, Spring AOP can hijack the executing method, and add extra functionality before or after the method execution” https://mkyong.com/spring/spring-aop-examples-advice/.

Examples that are mentioned in many sites are related to logging and verification and in most cases are temporal or have a cross-cutting concern, it means a same code that needs to be used in many places (temporal or not) could be hijacked without changing the original code.

How can I use it?

Well to start simple, we can create a custom annotation and then use AOP to understand how it works.

Dependencies

In this example, I’m using Spring Boot and the AOP starter that pulls the libraries required to implement with Aspects.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
</parent>

<dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
</dependencies>

Custom Annotation

I’m creating an annotation where I define that it only can be used by methods, and it will be available to the JVM at runtime.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogElapsedTime {

}

Example Service Component

Now, let’s create a component with a fake slow method and then include our annotation, until now we don’t have any Aspect configured yet.

@Component
public class ExampleService {

    @LogElapsedTime
    public void simulateSlowMethod() throws InterruptedException {
        Thread.sleep((int)(Math.random() * 1000));
    }
}

Example Aspect Component

Let’s create another component and in this case annotate also with @Aspect. In this component we need to create the pointcut and advice. I defined the advice to be @Around, it means I’m including extra code before and after the method execution. The advice argument is the pointcut and it says to apply when the @LogElapsedTime annotation is used.

The goal is to inject extra code to log how much time some methods take to finish. As you can see it’s very flexible and to include any method in this flow just needs an annotation.

@Aspect
@Component
@Slf4j
public class ExampleAspect {

    @Around("@annotation(com.sample.aop.LogElapsedTime)")
    public Object logElapsedTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        log.info(String.format("%s took %d ms", joinPoint.getSignature(), System.currentTimeMillis() - startTime));
        return proceed;
    }

}

Testing

The happy part, to test it and see how it works!

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class LogElapsedTimeTest {

    @Autowired
    private ExampleService service;

    @Test
    public void validateLogElapsedTimeAnnotation() throws InterruptedException {
        service.simulateSlowMethod();
    }

}

The complete code is available in my Github repository.

I hope it helps to understand better.

References:

[1] https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html

[2] https://mkyong.com/spring/spring-aop-examples-advice/

[3] https://www.baeldung.com/spring-aop-annotation

Spring IoC & Dependency Injection

It’s possible that in technical meetings you listened these terms, at the beginning they sound a little confusing and complex but the true is that they are just principles in Software Engineering.

Inversion of Control (IoC) is a concept to delegate the full responsibility to manage objects to some container or framework. Using this concept allows you more flexibility in your code as switching between implementations, decoupling your code and letting your code easy to be tested by the simple reason that the components will be isolated.

And one way to achieve it is using Dependency Injection (DI)

Dependency Injection is a pattern where the main idea is to have a separate object responsible to populates a dependency for another object.

Looking at an example below related to Books and Sections, if we don’t use DI and we need to create a book object, in this case we need to define a specific implementation for “sections” inside the constructor, and we create an unnecessary coupling:

public class Book {
    private List<Section> sections;
 
    public Book() {
        sections = new ArrayList<>();    
    }
}

On the other hand, if we use DI we can delegate the responsibility to create “sections” to other object, container or framework, and this lets your code more flexible.

public class Book {
    private List<Section> sections;
    public Book(List<Section> sections) {
        this.sections = sections;
    }
}

And using the Spring Framework and just using an “annotation” allows us to delegate the responsibility to another object that could has many implementations and letting easy to interchange between them:

public class Book {
    @Autowired
    private List<Section> sections;
}

I hope it allows you to understand better!

References:

https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring

https://www.martinfowler.com/articles/injection.html

Executar WildFly desde um diretório personalizado em Windows

Se precisam configurar um diretório fora da estrutura default do WildFly e inicializar o mesmo, podem seguir os seguintes passos:

1. Criar um diretório o qual será usado como referência no Wildfly:

C:\myDir\myApplication

2. Criar 2 links simbólicos:

  • Link para os arquivos de configuração do WildFly

    C:\myDir\myApplication\configuration

  • Link para o arquivo de deployment (war) do WildFly

    C:\myDir\myApplication\deployments

3. Supondo que o projeto esteja configurado com o hostname myNameHost (em geral é localhost), simplesmente abra um terminal e navegue até o diretório bin do WildFly e execute o comando abaixo:

$ ./standalone.bat -Djboss.server.base.dir=”C:\myDir\myApplication” -b myNameHost

4. O projeto contido no Servidor de Aplicações Wildfly deve inicializar de forma correta.

Referências:

https://www.wildfly.org/

 

Se quizer debugar tal aplicação, adicione no final do arquivo standalone.conf.bat as seguintes variáveis (neste exemplo o hostname é loghost, a versão do Java é Java7, e o nosso cluster local somente tem 1 servidor):

set “BIND_ADDRESS=loghost”
set “APPLICATION_PATH=C:\myDir\myApplication”
set “WILDFLY_MODE=standalone”
set “JAVA_HOME=C:\Program Files\Java\jdk1.7.0_80”
set “CLUSTER_CFG=-Doauth.initial_hosts=loghost[7600] -Doauth.cluster_size=1”
set “JAVA_OPTS=-server %CLUSTER_CFG% -Xms256m -Xmx1024m -Xss512k -XX:MaxPermSize=384m -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=86400000 -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Djava.net.preferIPv4Stack=true -Dnetworkaddress.cache.ttl=60 -Dnetworkaddress.cache.negative.ttl=5 -Djboss.server.default.config=standalone-full-ha.xml -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Duser.timezone=America/Sao_Paulo -Duser.language=pt -Duser.country=BR -Dfile.encoding=ISO-8859-1 -Djgroups.bind_addr=loghost -Djgroups.udp.mcast_addr=230.0.0.4 -Djgroups.udp.ip_ttl=32”
set “WILDFLY_USER=wildfly”
set “WILDFLY_HOME=C:\dir\wildfly-9.0.2.Final”
set “WILDFLY_CONSOLE=”C:\myDir\myApplication\logs”

Para enchergar o debug desde seu IDE (Eclipse ou IntelliJ) precisa somente adicionar um profile do tipo Remote debug e adicionar o host e porta loghost:7600, espero ajude, valeu.