Implementation of Spring-SpringAOP

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