目录

Spring-AOP

Spring-AOP

Spring-AOP

日志的两种编码方式

  • 硬编码

    • 硬编码是指在代码中 直接写死 具体的实现逻辑,没有使用任何代理模式或额外的抽象层。 当需要修改逻辑时,必须直接修改代码, 可维护性差,扩展性低
  • 静态代码:不修改源代码 低耦合

    • 定义: 创建一个代理对象,包装这个组件.以后业务的执行从代理对象出发不直接调用组件
    • 特点: 定义期间就指定好了互相代理关系
  • 动态代理:

    • 动态代理在 运行时生成代理类 ,无需手动编写代理类,适用于需要动态扩展功能的场景。

动态代理具体实现

1.创建目标对象接口
    public interface UserService {
    void saveUser();
}
2.创建实现类实现目标对象的方法
    public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("UserServiceImpl.saveUser");
    }
}
3.1创建动态代理工厂
    public class DynamicProxy {
    public static<T> T getProxyInstance(T target) {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    String name = method.getName();
                    LogUtil.logStart(name,args);
                    Object result = null;
                    try {
                        result = method.invoke(target, args);
                        LogUtil.logReturn(name, result);
                    }catch (Exception e){
                        LogUtil.logException(name,e);
                    }finally {
                        LogUtil.LogEnd(name);
                    }
                    return result;
                });
3.2使用@Aspect注解(声明切入点表达式)
    /**
 @Before: 前置通知在方法运行前
 @AfterReturning : 方法执行正常返回
 @AfterThrowing :方法抛出异常运行
 @After: 方法执行之后运行
 @Around: 环绕通知
 */
    @Pointcut("execution(int org.example.spring02aop.calculator.impl.MathCalculatorImpl.add(int,int))")
   @Before("execution(int *(int,int))")
    public  void logStart(){
        System.out.println("LogStart");
    }
    @After("execution(int *(int,int))")
    public  void logEnd(){
        System.out.println("logEnd");
    }
    @AfterReturning("execution(int *(int,int))")
    public  void logReturn(){
        System.out.println("logEnd");
    }
    @AfterThrowing("execution(int *(int,int))")
    public  void logException(){
        System.out.println("logException");
    }
3.3环绕通知
    @Aspect
@Component
public class AroundAspect {
    @Pointcut("execution(int org.example.spring02aop.calculator.impl.MathCalculatorImpl.add(int,int))")
    public void pointcut(){}
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint)throws Throwable{
        Object[] args = joinPoint.getArgs();//获取目标方法的参数

        System.out.println("环绕前置通知,args:"+ Arrays.toString(args));
        Object proceed = null;
        try {

         proceed = joinPoint.proceed();//继续执行目标方法`;反射 method.invoke();
            System.out.println("环绕返回通知,返回值:"+proceed);
        }catch (Exception e) {
            System.out.println("环绕异常通知"+e.getMessage());

        }finally {
            System.out.println("环绕后置通知,done");
        }
        return proceed;
        }
    }

总结 :若要统一日志管理,推荐使用环绕通知,代码入侵性低,复杂度低

切入点表达式的细节
规则示例说明
匹配所有方法execution(* *(..))任意类的任意方法
匹配某个类的所有方法execution(* com.example.service.UserService.*(..))UserService 的所有方法
匹配某个包下的所有方法execution(* com.example.service.*.*(..))service 包下所有类的方法
匹配某个包及子包下的所有方法execution(* com.example.service..*.*(..))service 及其子包下的所有方法
匹配特定方法execution(* com.example.service.UserService.getUser(..))getUser 方法
匹配某个返回类型的方法execution(String com.example.service.UserService.*(..))UserService 中返回 String 的方法
匹配某些参数的方法execution(* com.example.service.UserService.*(String))只有 一个 String 参数 的方法

上面例子写的就是只有名为add接收参数为两个int的方法才能触发此日志执行