Аспектно-ориентированное программирование и АОП в Spring Framework
Аспектно-ориентированное программирование (АОП), как следует из названия, использует аспекты в программировании. Это можно определить как разбиение кода на разные модули, также известное как модульность , где аспект является ключевым элементом модульности. Аспекты позволяют реализовать сквозные проблемы, такие как транзакция, ведение журнала, не являющееся центральным для бизнес-логики, без загромождения ядра кода для его функциональности. Это достигается путем добавления дополнительного поведения, которое является советом к существующему коду. Например, безопасность - это комплексная проблема, во многих методах в приложении могут применяться правила безопасности, поэтому повторение кода в каждом методе, определение функциональности в общем классе и управление должно применять эту функциональность во всем приложении.
Доминирующие фреймворки в АОП:
АОП включает в себя методы программирования и фреймворки, на которых поддерживается и реализуется модульность кода. Давайте посмотрим на три доминирующих фреймворка в АОП :
- AspectJ: Это расширение для программирования на Java, созданное в исследовательском центре PARC . Он использует синтаксис, подобный Java, и включает интеграцию IDE для отображения сквозной структуры. У него есть собственный компилятор и ткач, при его использовании можно использовать весь язык AspectJ.
- JBoss: это сервер приложений Java с открытым исходным кодом, разработанный JBoss, используемый для разработки Java.
- Spring: он использует конфигурацию на основе XML для реализации АОП, а также использует аннотации, которые интерпретируются с помощью библиотеки, предоставленной AspectJ для синтаксического анализа и сопоставления.
В настоящее время библиотеки AspectJ с фреймворком Spring доминируют на рынке, поэтому давайте разберемся, как Aspect-ориентированное программирование работает со Spring.
Как аспектно-ориентированное программирование работает с Spring:
Можно подумать, что вызов метода автоматически решит общие проблемы, но это не так. Просто вызов метода не вызывает совета (работы, которая должна быть выполнена). Spring использует механизм на основе прокси, т.е. он создает объект прокси, который будет обертывать исходный объект и принимать рекомендации, относящиеся к вызову метода. Прокси-объекты могут быть созданы либо вручную через фабричный компонент прокси, либо через автоматическую настройку прокси в XML-файле и уничтожены по завершении выполнения. Прокси-объекты используются для улучшения исходного поведения реального объекта.
Общая терминология в АОП:
- Aspect: The class which implements the JEE application cross-cutting concerns(transaction, logger etc) is known as the aspect. It can be normal class configured through XML configuration or through regular classes annotated with @Aspect.
- Weaving: The process of linking Aspects with an Advised Object. It can be done at load time, compile time or at runtime time. Spring AOP does weaving at runtime.
Let’s write our first aspect class but before that have a look at the jars required and the Bean configuration file for AOP.
package
com.aspect
import
org.aspectj.lang.annotation.Aspect;
import
Java.lang.RuntimeException;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
// Logging class is anotated with @Aspect
// and will contain advices.
@Aspect
class
Logging {
}
// The class ImplementAspect
// contains method Aspectcall
// and the advices will be applied
// on that method for demo.
public
class
ImplementAspect {
public
static
void
main(String args[])
{
Scanner sc =
new
Scanner(System.in);
System.out.println(
"my first aspect"
);
// **Add beanconfiguration file
// in your programme when executing.**
ApplicationContext ctx
=
new
ClassPathXmlApplicationContext(
"beanconfigfile.XML"
);
ImplementAspect call
= (ImplementAspect)ctx.getbean(
"aspect"
);
System.out.println(
"enter an integer"
);
int
a = sc.nextInt();
if
(a ==
1
) {
throw
new
RuntimeException(msg);
}
else
{
call.aspectCall();
}
call.myMethod();
}
public
void
aspectCall()
{
System.out.println(
"Applying advices"
+
" for the first time"
);
}
public
void
myMethod()
{
System.out.println(
"This is an"
+
" extra method"
);
}
}
- Advice: The job which is meant to be done by an Aspect or it can be defined as the action taken by the Aspect at a particular point. There are five types of Advice namely: Before, After, Around, AfterThrowing and AfterReturning. Let’s have a brief discussion about all the five types.
Types of Advices:
- Before: Runs before the advised method is invoked. It is denoted by @Before annotation.
- After: Runs after the advised method completes regardless of the outcome, whether successful or not. It is denoted by @After annotation.
- AfterReturning: Runs after the advised method successfully completes ie without any runtime exceptions. It is denoted by @AfterReturning annotation.
- Around: This is the strongest advice among all the advice since it wraps around and runs before and after the advised method. This type of advice is used where we need frequent access to a method or database like- caching. It is denoted by @Around annotation.
- AfterThrowing: Runs after the advised method throws a Runtime Exception. It is denoted by @AfterThrowing annotation.
Let’s implement all the five pieces of advice in our Aspect class Logger
// Program to show types of Advices
@Aspect
class
Logging {
// Implementing all the five pieces of advice
// to execute AfterThrowing advice enter integer value as 1.
// **Before**
@Before
(
"execution(public void com.aspect.ImplementAspect.aspectCall())"
)
public
void
loggingAdvice1()
{
System.out.println(
"Before advice is executed"
);
}
// **After**
@After
(
"execution(public void com.aspect.ImplementAspect.aspectCall())"
)
public
void
loggingAdvice2()
{
System.out.println(
"Running After Advice."
);
}
// **Around**
@Around
(
"execution(public void com.aspect.ImplementAspect.myMethod())"
)
public
void
loggingAdvice3()
{
System.out.println(
"Before and After invoking method myMethod"
);
}
// **AfterThrowing**
@AfterThrowing
(
"execution("
public
void
com.aspect.ImplementAspect.aspectCall())
")
public
void
loggingAdvice4()
{
System.out.println(
"Exception thrown in method"
);
}
// **AfterRunning**
@AfterReturning
(
"execution(public void com.aspect.ImplementAspect.myMethod())"
)
public
void
loggingAdvice5()
{
System.out.println(
"AfterReturning advice is run"
);
}
}
- JoinPoints: An application has thousands of opportunities or points to apply Advice. These points are known as join points. For example – Advice can be applied at every invocation of a method or exception be thrown or at various other points. But Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans).
Let’s see what does a joinpoint do in our @Aspect class(Logger)
// Program to show JoinPoints
@Aspect
class
Logging {
// Passing a JoinPoint Object
// into parameters of the method
// with the annotated advice
// enables to print the information
/// when the advice is executed
// with the help of toString() method
// present in it.
@Before
(
"execution(public void com.aspect.ImplementAspect.aspectCall())"
)
public
void
loggingAdvice1(JoinPoint joinpoint)
{
System.out.println(
"Before advice is executed"
);
System.out.println(joinpoint.toString());
}
}
- Pointcut: Since it is not feasible to apply advice at every point of the code, therefore, the selected join points where advice is finally applied are known as the Pointcut. Often you specify these pointcuts using explicit class and method names or through regular expressions that define a matching class and method name patterns. It helps in reduction of repeating code by writing once and use at multiple points, let’s see how.
// Program to shgw PointCuts
@Aspect
class
Logging {
// pointcut() is a dummy method
// required to hold @Pointcut annotation
// pointcut() can be used instead of writing line 1
// whenever required, as done in line 4.
// This prevents a repetition of code.
@Pointcut
(
"execution(public void com.aspect.ImplementAspect.aspectCall())"
)
// line 1
public
void
pointCut()
{
}
// pointcut() is used to avoid repeatition of code
@Before
(
"pointcut()"
)
public
void
loggingAdvice1()
{
System.out.println(
"Before advice is executed"
);
}
}
Attention reader! Don’t stop learning now. Get hold of all the important Java Foundation and Collections concepts with the Fundamentals of Java and Java Collections Course at a student-friendly price and become industry ready. To complete your preparation from learning a language to DS Algo and many more, please refer Complete Interview Preparation Course.