- Spring Essentials
- Shameer Kunjumohamed Hamidreza Sattari
- 1810字
- 2021-07-16 13:05:49
Aspect Oriented Programming
Most software applications usually have some secondary—but critical—features, such as security, transaction, and audit-logging, spanned across multiple logical modules. It would be a nice idea not to mix these cross-cutting concerns in your core business logic. Aspect Oriented Programming (AOP) helps you achieve this.
Object Oriented Programming (OOP) is about modularizing complex software programs, with objects as the fundamental units that hold your core business logic and data. AOP complements OOP to add more complex functionality transparently across modules of your application without polluting the original object structure. AOP stitches (weaves) cross-cutting concerns into your program, either at compile time or runtime, without modifying the base code itself. AOP lets the object-oriented program stay clean and just have the core business concerns.
Static and dynamic AOP
In AOP, the framework weaves the cross-cutting concerns into the main program transparently. This weaving process comes in two different flavors: static and dynamic. In the case of static AOP, as the name implies, Aspects are compiled directly into static files, that is, to the Java bytecode, on compilation. This method performs better, as there is no special interception at runtime. But the drawback is that you need to recompile the entire application every time you change anything in the code. AspectJ, one of the most comprehensive AOP implementations, provides compile-time weaving of Aspects.
In the case of dynamic AOP, the weaving process is performed dynamically at runtime. Different frameworks implement this differently, but the most general way of achieving this is using proxies or wrappers for the advised objects, allowing the Advice to be invoked as required. This is a more flexible method as you can apply AOP with varying behavior at runtime depending on data, which is not possible in the case of static AOP. There is no need for recompiling the main application code if you use XML files for defining your AOP constructs (schema-based approach). The disadvantage of dynamic AOP is a very negligible performance loss due to the extra runtime processing.
Spring AOP is proxy based, that is, it follows the dynamic flavor of AOP. Spring provides the facility to use static AOP by integrating with AspectJ too.
AOP concepts and terminology
Understanding AOP concepts and terms gives you an excellent starting point for AOP; it helps you visualize how and where AOP can be applied in your application:
- Aspect: The concern that cuts across multiple classes or modules. Transaction and security are examples. Spring Transaction is implemented as Aspects.
- Join point: A point during the execution of the program at which you want to insert additional logic using AOP. A method execution and a class instantiation are examples.
- Advice: The action taken by (the code or method that executes) the Aspect at a particular join point. Different types of advices include
before
,after
, andaround
advices. Typically, an Aspect has one or more Advices. - Pointcut: An expression that defines or matches a set of join points. The Advice associated with a pointcut executes at any join point it matches. Spring supports the AspectJ pointcut expression language by default. An example is
execution(* com.xyz.service.*.*(..))
. - Target object: The advised object. If you use dynamic AOP, this would be a proxied object.
- Weaving: Inserting Aspects into a target object to make it advised at compile time, load time or runtime. AspectJ supports compile-time weaving and Spring weaves at runtime.
- Introduction: The process by which you add a new method or field to an advised object, with or without making it implement an interface.
Spring AOP – definition and configuration styles
Spring provides a proxy-based dynamic implementation of AOP, developed purely in Java. It neither requires a special compilation process like AspectJ nor controls the class loader hierarchy, hence it can be deployed inside any Servlet container or application server.
Although not a full-blown AOP framework like AspectJ, Spring provides a simple and easy-to-use abstraction of most of the common features of AOP. It supports only method execution join points; field interception is not implemented. Spring provides tight integration with AspectJ, in case you want to advise very fine-grained Aspect orientation that Spring AOP doesn't cover by adding more AspectJ-specific features without breaking the core Spring AOP APIs.
Spring AOP uses standard JDK dynamic proxies for Aspect orientation by default. JDK dynamic proxies allow any interface (or set of interfaces) to be proxied. If you want to proxy classes rather than interfaces, you may switch to CGLIB proxies. Spring automatically switches to use CGLIB if a target object does not implement an interface.
Starting from Spring 2.0, you can follow either a schema-based approach or an @AspectJ
annotation style to write custom Aspects. Both of these styles offer fully typed Advice and use of the AspectJ pointcut language while still using Spring AOP for weaving.
XML schema-based AOP
When using schema-based AOP, you need to import aop
namespace tags into your application-context
file, as follows:
<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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> </beans>
@AspectJ annotation-based AOP
@AspectJ
refers to a style of declaring Aspects as regular Java classes that are annotated. Spring interprets the same annotations as AspectJ 5, using a library supplied by AspectJ for pointcut parsing and matching. Spring AOP has no dependency on the AspectJ compiler or weaver, though.
When using the @AspectJ
annotation style, you first need to enable @AspectJ
support in your Spring configuration, whether or not it is in the XML or Java configuration. Additionally, you need to make sure you add aspectjweaver.jar
in your classpath. Adding an @EnableAspectJAutoProxy
annotation to your Java @Configuration
annotation will enable @AspectJ
support in your project:
@Configuration @ComponentScan(basePackages = "com.springessentialsbook") @EnableAspectJAutoProxy public class AOPJavaConfigurator { ... }
Alternatively, if you use XML-based configuration, @AspectJ
support can be enabled by adding the <aop:aspectj-autoproxy/>
element in your application-context
file.
Declaring an @Aspect annotation
Your Aspect is a simple POJO, either annotated with @Aspect
(org.aspectj.lang.annotation.Aspect
) or declared as <aop:aspect/>
under the <aop:config>
section of your application-context
XML file. Remember, the class marked as @Aspect
should be declared as a Spring bean using either an annotation or <bean/>
declaration in your application context XML file.
Here is an annotated Aspect, a Spring component annotated as @Aspect
:
@Component("auditLoggerAspect") @Aspect public class AuditLoggerAspect { ... }
Note that @Aspect
is a Spring bean too. It can be any of the specializations of @Component
.
Now, let's take a look at the XML alternative for Aspect declaration:
<aop:config> <aop:aspect id="audLogAspect" ref="auditLoggerAspect"> </aop:config> <bean id="auditLoggerAspect" class="com...AuditLoggerAspect"/>
Aspects may have methods and fields, just like any other class. They may also contain pointcut, advice, and introduction (inter-type) declarations. Aspects themselves cannot be the target of Advice from other Aspects; they are excluded from auto-proxying.
Pointcuts
A pointcut comprises two parts, as shown in the following code snippet: a method signature (an empty method with a void
return type inside the Aspect
class) with any parameters and an expression that matches the exact method executions we are interested in. Remember, Spring AOP only supports method execution join points:
@Pointcut("execution(* com.springessentialsbook.service.TaskService.createTask(..))") //Pointcut expression private void createTaskPointCut() {} //Signature
The pointcut expression follows the standard AspectJ format. You may refer to the AspectJ pointcut expression reference for the detailed syntax. The following section gives you a strong foundation for constructing pointcuts for Spring AOP.
Pointcut designators
Spring AOP supports just a subset of the original AspectJ pointcut designators (PCDs) for use in pointcut expressions, as given in the following table:
In addition to the preceding table, Spring supports an extra non-AspectJ PCD, bean
, which is useful to directly refer to a Spring bean or a set of beans with a comma-separated list of beans using bean(idsOrNamesOfBean)
.
Note that the pointcuts intercept only public
methods due to the proxy nature of Spring AOP. If you want to intercept protected
and private
methods or even constructors, consider using AspectJ weaving (integrated with Spring itself) instead.
Pointcut examples
Pointcut expressions can be combined using &&
, ||
, and !
. You can refer to pointcut expressions by name, too. Let's see a few examples:
@Pointcut("execution(* com.taskify.service.*.*(..))") private void allServiceMethods() {} @Pointcut("execution(public * *(..))") private void anyPublicOperation() {} @Pointcut("anyPublicOperation() && allServiceMethods()") private void allPublicServiceMethods() {} @Pointcut("within(com.taskify.service..*)") private void allServiceClasses() {} @Pointcut("execution(* set*(..))") private void allSetMethods() {} @Pointcut("execution(* com.taskify.service.TaskService.*(..))") private void allTaskServiceMethods() {} @Pointcut("target(com.taskify.service.TaskService)") private void allTaskServiceImplMethods() {} @Pointcut("@within(org.springframework.transaction.annotation.Transactional)") private void allTransactionalObjectMethods() {} @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") private void allTransactionalAnnotatedMethods() {} @Pointcut("bean(simpleTaskService)") private void allSimpleTaskServiceBeanMethods() {}
An XML version of a pointcut definition goes like this:
<aop:config> ... <aop:pointcut id="allTaskServicePointCut" expression="execution(*com.taskify.service..TaskService.*(..))"/> </aop:config>
Advices
An Advice is the action that gets injected before, after, or around the method executions matched by the pointcut expression. The pointcut expression associated with an Advice could be a named or defined pointcut, as listed in the above examples, or a pointcut expression declared in place, that is, advices and pointcuts can be declared together.
Let's see an example for an Advice that refers to a pointcut expression named Pointcut
:
@Pointcut("execution(* com.taskify.service.TaskService.*(..))") private void allTaskServiceMethods() {} @Before("allTaskServiceMethods()") private void logBeforeAllTaskServiceMethods() { logger.info("*** logBeforeAllTaskServiceMethods invoked ! ***"); }
The following code listing combines both a join point and Advice in one go. This is the most common approach:
@After("execution(* com.taskigy.service.TaskService.*(..))") private void logAfterAllTaskServiceMethods() { logger.info("***logAfterAllTaskServiceMethods invoked ! ***"); }
The following table lists the available Advice annotations:
The @Around Advice
The @Around
Advice gives you more control over method execution, as the intercepted method essentially runs inside your Advice method. The first argument of the Advice must be ProceedingJoinPoint
. You need to invoke the proceed()
method of ProceedingJoinPoint
inside the Advice body in order to execute the target method; else, the method will not get called. After the method execution returns to you with whatever it returns back to your advice, do not forget to return the result in your Advice method. Take a look at a sample @Around
advice:
@Around("execution(* com.taskify.service.**.find*(..))") private Object profileServiceFindAdvice(ProceedingJoinPoint jPoint) throws Throwable { Date startTime = new Date(); Object result = jPoint.proceed(jPoint.getArgs()); Date endTime = new Date(); logger.info("Time taken to execute operation: " + jPoint.getSignature() + " is " + (endTime.getTime() - startTime.getTime()) + " ms"); return result; }
Accessing Advice parameters
There are two distinct ways of accessing the parameters of the method you are advising in the Advice method:
- Declaring a join point as the first argument
- Binding
args
in the pointcut definition
Let's see the first approach:
@Before("execution(* com.taskify.service.TaskService.createTask(..)") private void logBeforeCreateTaskAdvice(JoinPoint joinpoint) { logger.info("***logBeforeCreateTaskAdvice invoked ! ***"); logger.info("args = " + Arrays.asList(joinpoint.getArgs())); }
You can see that joinpoint.getArgs()
returns Object[]
of all the arguments passed to the intercepted method. Now, let's see how to bind named arguments to the Advice method:
@Before("createTaskPointCut() and args(name, priority, createdByuserId, assigneeUserId)") private void logBeforeCreateTaskAdvice(String name, int priority, int createdByuserId, int assigneeUserId) { logger.info("name = " + name + "; priority = " + priority + "; createdByuserId = " + createdByuserId); }
Note that the joinpoint
expression matches the arguments by name. You can have a joinpoint
object as an optional first argument in the method signature without specifying it in the expression: you will have both joinpoint
and arguments, enabling more manipulation.
- JavaScript高效圖形編程
- PHP 7底層設(shè)計(jì)與源碼實(shí)現(xiàn)
- 深入理解Django:框架內(nèi)幕與實(shí)現(xiàn)原理
- Scratch真好玩:教小孩學(xué)編程
- Instant QlikView 11 Application Development
- Python機(jī)器學(xué)習(xí)算法與實(shí)戰(zhàn)
- H5頁(yè)面設(shè)計(jì):Mugeda版(微課版)
- JavaScript程序設(shè)計(jì)(第2版)
- Java Web從入門到精通(第3版)
- 小程序從0到1:微信全棧工程師一本通
- 區(qū)塊鏈架構(gòu)之美:從比特幣、以太坊、超級(jí)賬本看區(qū)塊鏈架構(gòu)設(shè)計(jì)
- WebStorm Essentials
- 超好玩的Scratch 3.5少兒編程
- Java程序設(shè)計(jì)及應(yīng)用開發(fā)
- 讀故事學(xué)編程:Python王國(guó)歷險(xiǎn)記