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

项目1在线交流平台-3.开发交流社区核心功能模块-10.AOP思想解析-统一处理业务层日志

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

项目1在线交流平台-3.开发交流社区核心功能模块-10.AOP思想解析-统一处理业务层日志

文章目录

功能需求及处理策略1. AOP概念回顾

1.1 AOP思想1.2 AOP相关概念

==切面(ASPECT):==

==切入点(PointCut)==:==通知(Advice)==: ==目标(Target)==:==代理(Proxy)==:==连接点(JointPoint)==:

三种织入方式: 1.3 AOP实现方式

AspectJSpring AOP

SpringAop的两种动态代理方式

==JDK动态代理====CGLib动态代理== 2. Spring AOP实现示例

2.1 实现的三种具体方式

1)通过 Spring API 实现 2)自定义类来实现Aop-xml配置3) 自定义类来实现Aop-注解方式 2.2 使用注解方式实现Spring AOP示例

==@Aspect==

==@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")====Advice:具体位置及处理逻辑==

==@Before("pointCut()")====@After("pointCut()")====@AfterReturning("pointCut()")====@AfterThrowing("pointCut()")====@Around("pointCut()")== 测试结果 3. 对所有业务组件统计记录日志

==@Aspect 定义切面组件==定义系统组件逻辑

==@Pointcut定义切点====@Before业务组件逻辑之前记录日志==

==JoinPoint:连接点====RequestContextHolder.getRequestAttributes()====requestAttributes.getRequest()====joinPoint.getSignature()== 测试结果

参考牛客网高级项目教程

狂神说Spring教程笔记

功能需求及处理策略

1.前面的异常处理,只是在组件发生异常时,会触发控制器,有一定局限性

如果想在业务组件中记录日志,需要用到aop编程,以及springAop提供的组件 2.即,无需在每个业务组件处理记录日志等系统业务,只需要通过代理模式,统一使用aop处理增强业务逻辑3**.即将系统业务与处理业务分离,减少程序的耦合性、灵活性** 1. AOP概念回顾 1.1 AOP思想

Aspect Oriented Programing,

即面向方面(切面)编程。实现通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 AOP是一种编程思想,是对OOP的补充,

可以单独定义一个系统组件与业务组件独立,并管理业务组件,以代理模式对业务组件功能增强利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2 AOP相关概念

切面(ASPECT):

横切关注点 ,被模块化 的特殊对象。即,它是一个类。在此类中定义系统组件,用来管理和增强业务组件里面主要包括

切入点(PointCut)

先定位到要处理哪些方法即织入到哪些连接点 通知(Advice)

再定位到方法中具体的逻辑位置:前、后、返回后、异常、环绕以及在这些位置要增强哪些系统逻辑方法 切入点(PointCut)

切面通知 执行的 “地点”的定义。声明切点:要处理哪些类的哪些方法的哪些参数, 通知(Advice)

切面必须要完成的工作。即,它是类中的一个方法。定位到具体系统逻辑的具体位置

before:连接点之前After:连接点之后AfterReturning:返回值之后AfterThrowing:抛出异常时织入Around:连接点前后都织入 目标(Target)

被通知的原始真实对象。 代理(Proxy)

向目标对象应用通知之后创建的代理对象。动态代理一般都是对代理对象的操作 连接点(JointPoint)

具体每个业务组件织入的位置与切入点匹配的执行点 三种织入方式:

    编译时织入,需使用特殊的编译器

    此种方式,编译器一级准备好了,运行时快,但对运行时的变量情况不明确,不灵活

    装载时织入,需使用特殊的类装载器。

    运行时织入,需为目标生成代理对象。

    此种方式,灵活、但效率低些

1.3 AOP实现方式 AspectJ

AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。

因此不够灵活,可作为Spring AOP的补充 Spring AOP

Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点

其实大部分的实际开发中都是织入到方法中因此,此种方式性比较最高 Spring支持对AspectJ的集成。

如果有特殊织入点,比如不在方法中的,可以用AspectJ补充 SpringAop的两种动态代理方式

当目标对象有接口时,使用JDK动态代理当目标对象没有接口时,使用CGLib动态代理,在子类实例中织入代码 JDK动态代理

Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用此种方式,在接口的代理实例中织入代码。 CGLib动态代理

采用底层的字节码技术,在运行时创建子类代理实例。当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。

java支持多态,可以用子类对象代替父类的引用 2. Spring AOP实现示例 2.1 实现的三种具体方式 1)通过 Spring API 实现

直接继承实现接口,重新实现的方法

前置增强-连接点之前处理

public class Log implements MethodBeforeAdvice {
    //method : 要执行的目标对象的方法
    //objects : 被调用的方法的参数
    //Object : 目标对象
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
    }
}

后置增强-连接点之后处理

public class AfterLog implements AfterReturningAdvice {
    //returnValue 返回值
    //method被调用的方法
    //args 被调用的方法的对象的参数
    //target 被调用的目标对象
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName()
        +"的"+method.getName()+"方法,"
        +"返回值:"+returnValue);
    }
}

spring的文件中注册 , 并实现aop切入实现



    
    
    
    
    
    
        
        
        
        
        
    

2)自定义类来实现Aop-xml配置

可以自定义处理增强业务的类,然后在xml配置中注入到ioc容器中管理

public class DiyPointcut {
    public void before(){
        System.out.println("---------方法执行前---------");
    }
    public void after(){
        System.out.println("---------方法执行后---------");
    }
}




    
    
        
        
        
    

3) 自定义类来实现Aop-注解方式

与上述逻辑类似,采用注解方式,可以不用在xml配置中配置,直接使用注解注入即可

本项目采用的是第三种,具体示例如下:

2.2 使用注解方式实现Spring AOP示例 @Aspect

注明是切面组件,结合@Component,交给IOC管理

package com.nowcoder.community.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AlphaAspect {
}
@Pointcut(“execution(* com.nowcoder.community.service..(…))”)

声明切点-并定义织入位置

位置可以在直接的值中定义,比较灵活

    
    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointCut() {}
Advice:具体位置及处理逻辑

示例中,采用打印到控制台的方法代理日志处理 @Before(“pointCut()”)

连接点之前织入

注解的值传入之前定义好的切点 pointCut()

    
    @Before("pointCut()")
    public void before() {
        System.out.println("before");
    }
@After(“pointCut()”)

连接点之后织入

注解的值传入之前定义好的切点 pointCut()

    
    @After("pointCut()")
    public void after() {
        System.out.println("after");
    }
@AfterReturning(“pointCut()”)

方法返回后织入

注解的值传入之前定义好的切点 pointCut()

    
    @AfterReturning("pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning");
    }
@AfterThrowing(“pointCut()”)

方法出现异常时织入

注解的值传入之前定义好的切点 pointCut()

    
    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }
@Around(“pointCut()”)

连接点前后均织入

注解的值传入之前定义好的切点 pointCut()

	
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before");
        Object obj = joinPoint.proceed();
        System.out.println("around after");
        return obj;
    }
测试结果

3. 对所有业务组件统计记录日志 @Aspect 定义切面组件

工厂模式创建日志对象

@Component
@Aspect
public class ServiceAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
}
定义系统组件逻辑 @Pointcut定义切点
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointCut() {}
@Before业务组件逻辑之前记录日志 JoinPoint:连接点

通过连接点,可定位到具体的业务组件子类代理对象

@Before("pointCut()")
public void before(JoinPoint joinPoint) {
}
RequestContextHolder.getRequestAttributes()

上下文组件容器可以获取请求上下文

        // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
        // 获取用户ip
        // 获取请求上下文
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
requestAttributes.getRequest()

获取请求

// 获取请求
HttpServletRequest request = requestAttributes.getRequest();
joinPoint.getSignature()

AOP编程过程中的Signature接口

此方法可以获取原始方法的反射信息接口

// 连接点织入的业务组件子类代理(原始方法)信息-类全路径名-方法名
String target = joinPoint.getSignature().getDeclaringTypeName() +
        "." + joinPoint.getSignature().getName();
@Component
@Aspect
public class ServiceAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    
    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointCut() {}

    
    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
        // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
        // 获取用户信息
        // 获取请求上下文
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取请求
        HttpServletRequest request = requestAttributes.getRequest();
        // 用户ip
        String ip = request.getRemoteHost();
        // 当前时间
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        // 连接点织入的业务组件子类代理(原始方法)信息-类全路径名-方法名
        String target = joinPoint.getSignature().getDeclaringTypeName() +
                "." + joinPoint.getSignature().getName();
//        String target = joinPoint.getSignature().toString();
        // 记录日志
        logger.info(String.format("用户[%s], 在[%s], 访问了[%s].", ip, now, target));
    }
}
测试结果

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

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

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