Understanding of Spring AOP
OOP stands for object-oriented programming, which is a programming idea, and AOP stands for aspect-oriented programming, which is also a programming idea.
Spring AOP: Technical support provided by Spring to make it easier for programmers to implement aspect-oriented programming.
A set of mechanisms provided by Spring make it easier for us to perform AOP. This mechanism is Spring AOP.
Extension: Spring is not the first to use annotations to define Pointcut and Advice. The first is AspectJ. Technologies such as JBoss 4.0 and aspectwerkz also provide support for AOP.
Spring relies on AspectJ. Spring thinks that the @Before, @Around and other annotations in AspectJ are easier to use, so it uses these annotations directly, but the underlying analysis of the annotations is done by Spring itself.
So when we use Spring, if you want to use @Before, @Around and other annotations, you need to introduce the aspecj related jar package separately:
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5' compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
Note: AspectJ (itself is also a project) modifies the bytecode during compilation. It can be understood that @Before annotations will be parsed during compilation, and then the proxy logic will be obtained. Added to the bytecode of the proxy class, so if you want to use AspectJ technology to generate proxy objects, you need to use a separate AspectJ compiler. The AspectJ compiler is rarely used in the project, but the @Before annotations are used. During the starting process of Spring, these annotations will be parsed, and then the dynamic proxy mechanism will be used to generate proxy objects.
Concepts in AOP
Spring official website:
Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology
Translation: These concepts in AOP are not specific to Spring. Unfortunately, the concepts in AOP are not particularly intuitive, but if Spring redefined itself that could lead to more confusion.
1. Aspect: Represents Aspect. For example, the class annotated with @Aspect is the aspect. Pointcut, Advice, etc. can be defined in the aspect.
2. Join point: Represents Join point, which represents a point in the execution process of a program, such as the execution of a method, such as the handling of an exception. In Spring AOP, a join point usually represents a method execution
3. Advice: indicates notification, indicating the action taken at a specific connection point. Many AOP frameworks, including Spring, use Interceptor interceptors to implement Advice and maintain an Interceptor chain around the connection point.
4. Pointcut: Represents pointcut, used to match one or more connection points. Advice and pointcut expressions are associated together, and Advice will be executed on the path that matches the pointcut expression. on connection point
5. Introduction: You can use @DeclareParents to add an interface to the matching class and specify a default implementation
6. Target object: Target object, proxy object
7. AOP proxy: represents proxy factory, which is used to create proxy objects. In Spring Framework, it is either a JDK dynamic proxy or a CGLIB proxy.
8. Weaving: means weaving, and represents the action of creating a proxy object. This action can occur at compile time (such as Aspejctj), or at runtime, such as Spring AOP
Advice’s corresponding API in Spring AOP
Five annotations are used in Aspject to define Advice, indicating agent logic and execution timing; Spring provides implementation classes similar to execution timing:
Aspject annotation (proxy logic, execution timing) | Spring implementation class (similar to execution timing) | Spring parses the annotation into the corresponding Advice class |
---|---|---|
@Before | Interface MethodBeforeAdvice, inherits the interface BeforeAdvice | AspectJMethodBeforeAdvice (actually MethodBeforeAdvice) |
@AfterReturning | Interface AfterReturningAdvice | AspectJAfterReturningAdvice (actually AfterReturningAdvice) |
@AfterThrowing | Interface ThrowsAdvice | AspectJAfterThrowingAdvice (actually MethodInterceptor) |
@After | Interface AfterAdvice | AspectJAfterAdvice (actually MethodInterceptor) |
@Around | Interface MethodInterceptor | AspectJAroundAdvice (actually MethodInterceptor) |
Usage of TargetSource
In daily AOP, the proxy object is the Bean object, which is created by BeanFactory.
Spring AOP provides a TargetSource mechanism, which can use custom logic to create proxy objects.
When the @Lazy annotation is added to a property, a proxy object will be generated and assigned to the property:
/** *Complete implementation of the * {@link org.springframework.beans.factory.support.AutowireCandidateResolver} strategy * interface, providing support for qualifier annotations as well as for lazy resolution * driven by the {@link Lazy} annotation in the {@code context.annotation} package. * * @author Juergen Hoeller * @since 4.0 */ public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver { @Override @Nullable public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { // Determine whether it is lazy injection (@Autowired + @Lazy) // If so, a proxy object will be generated and injected into the property during injection, so lazy injection does not mean that the property is null. return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); } protected boolean isLazy(DependencyDescriptor descriptor) { for (Annotation ann : descriptor.getAnnotations()) { Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); if (lazy != null & amp; & amp; lazy.value()) { return true; } } MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { Method method = methodParam.getMethod(); if (method == null || void.class == method.getReturnType()) { Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class); if (lazy != null & amp; & amp; lazy.value()) { return true; } } } return false; } protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) { BeanFactory beanFactory = getBeanFactory(); Assert.state(beanFactory instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory"); final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory; TargetSource ts = new TargetSource() { @Override public Class<?> getTargetClass() { return descriptor.getDependencyType(); } @Override public boolean isStatic() { return false; } @Override public Object getTarget() { Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null); Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null); if (target == null) { Class<?> type = getTargetClass(); if (Map.class == type) { return Collections.emptyMap(); } else if (List.class == type) { return Collections.emptyList(); } else if (Set.class == type || Collection.class == type) { return Collections.emptySet(); } throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(), "Optional dependency not present for lazy injection point"); } if (autowiredBeanNames != null) { for (String autowiredBeanName : autowiredBeanNames) { if (dlbf.containsBean(autowiredBeanName)) { dlbf.registerDependentBean(autowiredBeanName, beanName); } } } return target; } @Override public void releaseTarget(Object target) { } }; // ProxyFactory generates proxy objects and uses TargetSource //So when the proxy object executes a method, it calls the getTarget() method of TargetSource to get a proxy object in real time. ProxyFactory pf = new ProxyFactory(); pf.setTargetSource(ts); Class<?> dependencyType = descriptor.getDependencyType(); if (dependencyType.isInterface()) { pf.addInterface(dependencyType); } return pf.getProxy(dlbf.getBeanClassLoader()); } }
Introduction (@DeclareParents)
Spring official website’s introduction to Introduction and related concerns @DeclareParents:
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects. An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name).
What is the use of Introduction?
You can introduce a new interface to an existing class and perform some extended behaviors without modifying the original class.
For example, if a service is being provided in production and you want to add a verification function, you can implement it through the @DeclareParents annotation.
How to use @DeclareParents annotation?
//The first step is to add an interface package com.gax.aop; public interface Verifier { boolean validate(User user); } //The second step is to add an implementation class to the interface package com.gax.aop; public class BasicVerifier implements Verifier { @Override public boolean validate(User user) { if (user.getName().equals("gc") & amp; & amp; user.getPass().equals("6174")) { return true; } return false; } } // The third step is to use the @DeclareParents annotation to associate the new interface with the original class. @Aspect @Component public class MyAspect { @DeclareParents(value = "com.gax.service.UserService", defaultImpl = com.gax.aop.BasicVerifier.class) public Verifier verifer; } //The fourth step, test the effect public class Test { public static void main(String[] args) { User user = new User(); user.setPass("6174888"); user.setName("gc"); //Create a Spring container AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = (UserService) applicationContext.getBean("userService"); \t\t // Note that Verifier here is an interface Verifier verifier = (Verifier) userService; // Only after verification is passed can the service be provided. if(verifier.validate(user)) { userService.service(); } } } // AppConfig specifies the scan package @ComponentScan(value = "com.gax") //@EnableAspectJAutoProxy @Import(AnnotationAwareAspectJAutoProxyCreator.class) public classAppConfig { } @Data public class User { private String name; private String pass; }
@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
In some cases, the above two ways of writing are equivalent. The @EnableAspectJAutoProxy annotation actually registers an AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator is actually a BeanPostProcessor, which can parse AspectJ annotations during Spring startup.
Reference article: https://www.cnblogs.com/powerwu/articles/5170861.html
LoadTimeWeaver
Reference article: https://www.cnblogs.com/davidwang456/p/5633609.html