自定义注解切面针对类中所有方法增强
自定义注解切面针对类中所有方法增强
一.背景
今天中午带我的人,突然问我说之前你不是搞过自定义注解做切面,完成一些非业务的逻辑吗,我记得你遇到过一个导致切面不生效的问题,当时好像是因为this调用导致的,你还知道有哪些会导致失效吗,然后我说只要这个对象非spring管理的这个切面就不会生效。然后他说我遇到一个问题,想增强一个类里面所有的方法但是无论如何都走不进去,出于好奇的我,吃完午饭就开始研究了一下。
二.过程
2.1 Google查询问题
首先归纳总结一下问题所在: 自定义注解加上类上不生效!然后查询google给出的第一条答案就是
@within和**@annotation用法不对会导致不生效,@within** 是对象级别的意思是作用于整个类,而**@annotation** 方法级别只作用方法,如果使用错了会导致不生效.
2.2 编码验证
2.2.1 自定义注解
/**
 * @Author jmle
 * @Date 2023/12/19 12:48
 * @Version 1.0
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
 
2.2.2 自定义切面
package com.ljm.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 * @Author jmle
 * @Date 2023/12/19 12:41
 * @Version 1.0
 */
@Aspect
@Component
public class MyAspect {
    @Pointcut("@within(com.ljm.annotation.MyAnnotation)")
    public void myAspect() {
    }
    @Around(value = "myAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 处理Aop逻辑
        String name = joinPoint.getSignature().getName();
        System.out.printf("%s,start execute aop %n",name);
        return joinPoint.proceed();
    }
}
 
2.2.3 测试验证
经过测试验证使用**@within(com.ljm.annotation.MyAnnotation)可以使类中的方法都生效,但是使用@annoation(com.ljm.annotation.MyAnnotation)是不会生效的,后续就和带我的人求证他写的代码,发现他的切入点写的确实是@annoation(com.ljm.annotation.MyAnnotation)**,正当我内心以为我们找到了问题之后,更改了代码发现还是没有生效,虽然这确实是一个问题所在,但是还有其他问题导致不生效!
2.3 模拟场景
2.3.1 定义业务接口
package com.ljm.handler;
import com.ljm.pojo.HandlerEnum;
/**
 * @Author jmle
 * @Date 2023/12/19 14:47
 * @Version 1.0
 */
public interface IHandler {
     // 业务处理逻辑
     void handler();
     // 业务处理类型
     HandlerEnum getHandlerType();
}
 
2.3.2 定义实现
FirstHandler
package com.ljm.handler.impl;
import com.ljm.annotation.MyAnnotation;
import com.ljm.handler.IHandler;
import com.ljm.pojo.HandlerEnum;
import org.springframework.stereotype.Service;
/**
 * @Author jmle
 * @Date 2023/12/19 14:56
 * @Version 1.0
 */
@Service
public class FirstHandler implements IHandler {
    @Override
    public void handler() {
        String simpleName = this.getClass().getSimpleName();
        System.out.printf("%s start handler business%n", simpleName);
    }
    @Override
    public HandlerEnum getHandlerType() {
        return HandlerEnum.FIRST_HANDLER;
    }
}
 
SecondHandler
package com.ljm.handler.impl;
import com.ljm.annotation.MyAnnotation;
import com.ljm.handler.IHandler;
import com.ljm.pojo.HandlerEnum;
import org.springframework.stereotype.Service;
/**
 * @Author jmle
 * @Date 2023/12/19 14:56
 * @Version 1.0
 */
@Service
@MyAnnotation
public class SecondHandler implements IHandler {
    @Override
    public void handler() {
        String simpleName = this.getClass().getSimpleName();
        System.out.printf("%s start handler business%n", simpleName);
    }
    @Override
    public HandlerEnum getHandlerType() {
        return HandlerEnum.SECOND_HANDLER;
    }
}
 
ThridHandler
package com.ljm.handler.impl;
import com.ljm.annotation.MyAnnotation;
import com.ljm.handler.IHandler;
import com.ljm.pojo.HandlerEnum;
import org.springframework.stereotype.Service;
/**
 * @Author jmle
 * @Date 2023/12/19 14:56
 * @Version 1.0
 */
@Service
@MyAnnotation
public class ThirdHandler implements IHandler {
    @Override
    public void handler() {
        String simpleName = this.getClass().getSimpleName();
        System.out.printf("%s start handler business%n", simpleName);
    }
    @Override
    public HandlerEnum getHandlerType() {
        return HandlerEnum.THIRD_HANDLER;
    }
}
 
2.3.3 定义业务枚举类型
package com.ljm.pojo;
import java.util.Arrays;
import java.util.Optional;
/**
 * @Author jmle
 * @Date 2023/12/19 14:48
 * @Version 1.0
 */
public enum HandlerEnum {
    FIRST_HANDLER(0),
    SECOND_HANDLER(1),
    THIRD_HANDLER(2);
    private int value;
    HandlerEnum(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
    public static HandlerEnum getByValue(int value) {
        Optional<HandlerEnum> optional = Arrays.stream(values()).filter(p -> p.getValue() == value).findFirst();
        if (optional.isPresent()) {
            return optional.get();
        }
        return null;
    }
}
 
2.3.4 定义工厂
package com.ljm.factory;
import com.ljm.handler.IHandler;
import com.ljm.pojo.HandlerEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @Author jmle
 * @Date 2023/12/19 15:03
 * @Version 1.0
 */
@Component
public class HandlerFactory {
    private final Map<HandlerEnum, IHandler> HANDLER_MAP = new ConcurrentHashMap<>();
    public IHandler getHandlerByType(HandlerEnum handlerEnum) {
        return HANDLER_MAP.get(handlerEnum);
    }
    @Autowired
    public void HandlerFactory(List<IHandler> iHandlerList) {
        for (IHandler iHandler : iHandlerList) {
            HANDLER_MAP.put(iHandler.getHandlerType(),iHandler);
        }
    }
}
 
HandlerFactory这个工厂是交给spring管理的,目的其实是为了业务方根据传递进来的业务code,选择相应的处理handler,其实本质上是策略模式的应用,原先带我的写的是静态的工厂类,没有交给spring管理,生成的bean虽然都是spring容器生成的,但是没有被增强 所以导致切面没有生效,spring增强的Bean对象名中会带有BySpringCGLB例如下面这个SecondHandler就是被Spring AOP增强的bean

 而FirstHandler非spring AOP增强,不带BySpringCGLB

2.3.5 定义测试Controller
package com.ljm.controller;
import com.ljm.factory.HandlerFactory;
import com.ljm.handler.IHandler;
import com.ljm.pojo.HandlerEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @Author jmle
 * @Date 2022/10/27 16:55
 * @Version 1.0
 */
@RestController
@RequestMapping
public class MinioController {
    @Autowired
    private HandlerFactory handlerFactory;
    @GetMapping("/test")
    public void callTest(@RequestParam Integer type) {
        HandlerEnum handlerEnum = HandlerEnum.getByValue(type);
        if (handlerEnum == null) {
            return;
        }
        IHandler handlerByType = handlerFactory.getHandlerByType(handlerEnum);
        if (handlerByType == null) {
            return;
        }
        handlerByType.handler();
    }
}
 
2.3.6 定义启动类
package com.ljm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
 * @Author jmle
 * @Date 2022/6/23 15:47
 * @Version 1.0
 */
@SpringBootApplication
@EnableAspectJAutoProxy
public class MyLoveApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyLoveApplication.class);
    }
}
 
2.3.7 测试验证
当我们调用一次http://localhost:8081/test?type=1时,因为SecondHandler这个类是被增强过的所以这个类里面的所有方法都会打印下面这句话
handler,start execute aop 
 
然后再打印
SecondHandler start handler business
 
而当我们调用http://localhost:8081/test?type=0,因为FirstHandler这个没有被增强过只会打印下面这句话
FirstHandler start handler business
 
三.总结
他山之石,可以攻玉,以上均为伪代码,可能没有遵循代码规范,旨在阐述原因!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!