栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring源码解析-SpringAOP入门级实战

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring源码解析-SpringAOP入门级实战

《Spring源码解析-SpringAOP入门级实战》首发牧码人博客转发请加此提示

Spring源码解析-SpringAOP入门级实战 题外话

大家好我是鸽子王-牧码人,鸽了大半年,一直沉迷于加班的快乐之中。近半年其实挺迷茫,一直在加班,写一堆业务代码。感觉自己技术栈越走越窄了。没有很大的进步。当然写业务代码期间也碰到了不少棘手问题,也优化了不少问题。最近腾了点时间还是决定把它分享给大家。

1.引入

Spring AOP一直是面试必考点之一,很多人尽管没有使用过,但是也被面试官吊打过不少了,所以对于AOP的一些概念的东西也就倒背如流了。这一点我面试人的过程中我也会经常去问,很多人对于切点,通知等专业词汇张口就来。一问AOP能用来做啥,大家跟约定好一样,就是记日志。我今天就给大家讲讲怎么使用AOP来实现记录日志的功能。

2.应用场景

最近接了一个需求,大致就是上传文件到FTP服务器上。需要记录文件名,上传时间,上传状态、数量等。需求不是很难。我这边大概有10个定时任务是做这种类似的功能,因为上传的文件内容都是各不相同的。

3.需求分析

对于这个需求无非就是一个记录日志的功能,但是量很多有10几个这种类似的功能。但是记录日志这个方法都是通用的。虽然每个上传文件处理逻辑是不一样的,但是记录日志这个日志是通用的。上传前要记录一下上传中,上传之后,把数据量,上传状态改为成功。

4.实现方式 4.1 传统方式
 try {
     	           InterfaceLog interfaceLog = new InterfaceLog();
					interfaceLog.setTableCode("ANLYSIS_BUSINESS_DISCOUNT_DATA");
					interfaceLog.setTableName("分产品折扣月表");
					interfaceLog.setNum(0);
					interfaceLog.setIsSuccess("上传中");
					interfaceLog.setCreateTime(new Date());
                 	interfaceLogService.save(interfaceLog);
                    //保存文件数据
					int num =upload();
					interfaceLog.setNum(num);
					interfaceLog.setIsSuccess("成功");
					interfaceLogService.update(interfaceLog);
                } catch (Exception e) {
					interfaceLog.setIsSuccess("失败");
					interfaceLog.setFailReason(e.getMessage());
					interfaceLogService.update(interfaceLog);
                    e.printStackTrace();
                }

这是前同事的代码,这是另一个功能,解析文件记录日志的功能。这种代码,我看到了大概50个类左右。

4.2AOP方式实现(注解方式实现)

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface FileIssueLogAnnotation {

	
	String value();
}


创建Aspect容器

@Slf4j  
@Aspect //核心注解,定义切面容器
@EnableAsync//核心注解,支持异步
@AllArgsConstructor
@Component
public class SysFileIssueLogAspect {

    @Autowired
    SysFileIssueLogService logService;

	@SneakyThrows
	@Around("@annotation(sysFileIssueLog)")
	public Object around(ProceedingJoinPoint point,
                         FileIssueLogAnnotation sysFileIssueLog) {
        String strClassName = point.getTarget().getClass().getName();
        String strMethodName = point.getSignature().getName();
        log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);

        Object[] args = point.getArgs();

        SysFileIssueLog log = new SysFileIssueLog();

        for (Object arg : args) {
            if (arg instanceof AbstractFileIssueStParam) {
                AbstractFileIssueStParam queryStParam = (FileIssueStParam) arg;
                log.setFileName(queryStParam.getFileName());
                log.setPeriodDate(queryStParam.getPeriodLocalDate());
                log.setDecision(
                        queryStParam.getDecision()
                );
                log.setType(sysFileIssueLog.value());
            }
        }
        logService.saveFileLog(log);
        Object obj = null;
        try {
            obj = point.proceed();

            if (obj instanceof R) {
                R r = (R) obj;

                if (r.getData()) {
                    log.setIssueState(FileIssueLogEnum.SUCCESS.getType());
                } else {
                    log.setIssueState(FileIssueLogEnum.FAIL.getType());
                    log.setErrorLog(r.getMsg());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.setErrorLog(e.getMessage());
            log.setIssueState(FileIssueLogEnum.FAIL
                    .getType());
        }
        log.setUpdatedTime(DateUtil.toLocalDateTime(new Date()));
        logService.updateById(log);
        return obj;
    }

}

4.3在需要使用的地方加上注解
public interface RemoteFileService {

    
    @FileIssueLogAnnotation(value = "xx文件")
    @PostMapping("/sthidata/genTravelCheckDataFile")
    R genTravelCheckDataFile(@RequestBody FileIssueStParam param,
                                      @RequestHeader(SecurityConstants.FROM) String from);


}

以上就是通过注解实现AOP记录日志功能,只要是加上了FileIssueLogAnnotation这注解的方法,便会被切面容器管理到。

AOP机制浅谈

AOP也称面向切面,面向切面编程最大的方便就是减少代码冗余。其实面向切面的原理就是JDK中的动态代理。代理一个方法,处理这个方法前做一些事,处理完后在做一些事。只不过Spring团队利用这个机制,把代码进行了封装。更加方便大家使用。

  • jdk动态代理

    定义处理逻辑

  
public interface IHello{
    void sayHello();
}

public class HelloImpl implements IHello {
    @Override
    public void sayHello() {
        System.out.println("Hello JAVA");
    }
}
定义代理类
 
  
public class MyInvocationHandler implements InvocationHandler {
 
    
    private Object target;
 
    public MyInvocationHandler(Object target){
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("------代理前-------------");
        // 执行相应的目标方法
        Object rs = method.invoke(target,args);
        System.out.println("------代理后-------------");
        return rs;
    }
}
实现测试
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
 

 
 
public class MyProxyTest {
    public static void main(String[] args)
            throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        // =========================第一种==========================
        // 1、生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 2、获取动态代理类
        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
        // 3、获得代理类的构造函数,并传入参数类型InvocationHandler.class
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        // 4、通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
        IHello iHello1 = (IHello) constructor.newInstance(new MyInvocationHandler(new HelloImpl()));
        // 5、通过代理对象调用目标方法
        iHello1.sayHello();
 
        // ==========================第二种=============================
        
        IHello  iHello2 = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), // 加载接口的类加载器
                new Class[]{IHello.class}, // 一组接口
                new MyInvocationHandler(new HelloImpl())); // 自定义的InvocationHandler
        iHello2.sayHello();
    }
}

//执行结果
------代理前-------------
Hello JAVA
------代理后-------------

所以我认为的AOP是一个处理思想。最关键的点还是了解他的思想。了解思想后,你便可以使用它来解决一些问题。

  • SpringAop的一些专业术语

    • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
    • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
    • 切点(PointCut): 可以插入增强处理的连接点。
    • 切面(Aspect): 切面是通知和切点的结合。
    • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
    • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

    概念性的东西了解一下就行,不同的时候有不同的理解。

  • SpringAop的一些常用注解

    • @Aspect : 定义切面类。(类级)
    • @Pointcut(): 定义一个切点供通知使用。(方法级)
    • @Before: 前置通知注解。(方法级)
    • @After: 后置通知注解。(方法级)
    • @AfterReturning:切点类处理完毕后,返回值时调用。(方法级)
    • @Around:环绕通知。这个是Before跟After结合。(方法级)
总结

SpringAOP是学习Spring的核心。AOP这个概念,对于我们日常开发也是非常重要的。会写一个AOP的例子并不难,重要的往往是理解这种代码思想。并且在日常的业务代码中灵活使用它。接下来,有时间的话,我会跟着大家一起看一下Spring是怎么使用JDk的动态代理,实现AOP的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/275402.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号