Spring AOP详细介绍

Spring AOP详细介绍AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。一AOP的基本概念(1)Asp

大家好,又见面了,我是你们的朋友全栈君。

AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。

一 AOP的基本概念

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知

(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

二 Spring AOP

Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。

三 基于注解的AOP配置方式

1.启用@AsjectJ支持

在applicationContext.xml中配置下面一句:

<aop:aspectj-autoproxy />

2.通知类型介绍

(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名

来访问目标方法中所抛出的异常对象

(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式

(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

3.例子:

<span role="heading" aria-level="2">Spring AOP详细介绍

(1)Operator.java –> 切面类

@Component
@Aspect
public class Operator {
    
    @Pointcut("execution(* com.aijava.springcode.service..*.*(..))")
    public void pointCut(){}
    
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("AOP Before Advice...");
    }
    
    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint){
        System.out.println("AOP After Advice...");
    }
    
    @AfterReturning(pointcut="pointCut()",returning="returnVal")
    public void afterReturn(JoinPoint joinPoint,Object returnVal){
        System.out.println("AOP AfterReturning Advice:" + returnVal);
    }
    
    @AfterThrowing(pointcut="pointCut()",throwing="error")
    public void afterThrowing(JoinPoint joinPoint,Throwable error){
        System.out.println("AOP AfterThrowing Advice..." + error);
        System.out.println("AfterThrowing...");
    }
    
    @Around("pointCut()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("AOP Aronud before...");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("AOP Aronud after...");
    }
    
}

(2)UserService.java –> 定义一些目标方法

@Service
public class UserService {
    
    public void add(){
        System.out.println("UserService add()");
    }
    
    public boolean delete(){
        System.out.println("UserService delete()");
        return true;
    }
    
    public void edit(){
        System.out.println("UserService edit()");
        int i = 5/0;
    }
    
    
}

(3).applicationContext.xml

<context:component-scan base-package="com.aijava.springcode"/>
    
<aop:aspectj-autoproxy />

(4).Test.java

public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = (UserService) ctx.getBean("userService");
        userService.add();
    }
}

上面是一个比较简单的测试,基本涵盖了各种增强定义。注意:做环绕通知的时候,调用ProceedingJoinPoint的proceed()方法才会执行目标方法。

4.通知执行的优先级

进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After。

注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!同时使用也没啥意义。

5.切入点的定义和表达式

切入点表达式的定义算是整个AOP中的核心,有一套自己的规范

Spring AOP支持的切入点指示符:

(1)execution:用来匹配执行方法的连接点

A:@Pointcut(“execution(* com.aijava.springcode.service..*.*(..))”)

第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示

方法的任意参数个数

B:@Pointcut(“within(com.aijava.springcode.service.*)”)

within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点

C:@Pointcut(“this(com.aijava.springcode.service.UserService)”)

this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService

D:@Pointcut(“bean(userService)”)

bean也是非常常用的,bean可以指定IOC容器中的bean的名称

6.基于XML形式的配置方式

开发中如果选用XML配置方式,通常就是POJO+XML来开发AOP,大同小异,无非就是在XML文件中写切入点表达式和通知类型

例子:

(1)Log.java

public class Log {

    private Integer id;

    //操作名称,方法名
    private String operName;

    //操作人
    private String operator;

    //操作参数
    private String operParams;

    //操作结果 成功/失败
    private String operResult;

    //结果消息
    private String resultMsg;

    //操作时间
    private Date operTime = new Date();

    setter,getter

}

(2).Logger.java

/**
 * 日志记录器 (AOP日志通知)
 */
public class Logger {
    
    @Resource
    private LogService logService;
    
    public Object record(ProceedingJoinPoint pjp){
        
        Log log = new Log();
        try {
            log.setOperator("admin");
            String mname = pjp.getSignature().getName();
            log.setOperName(mname);
            
            //方法参数,本例中是User user
            Object[] args = pjp.getArgs();
            log.setOperParams(Arrays.toString(args));
            
            //执行目标方法,返回的是目标方法的返回值,本例中 void
            Object obj = pjp.proceed();
            if(obj != null){
                log.setResultMsg(obj.toString());
            }else{
                log.setResultMsg(null);
            }
            
            log.setOperResult("success");
            log.setOperTime(new Date());
            
            return obj;
        } catch (Throwable e) {
            log.setOperResult("failure");
            log.setResultMsg(e.getMessage());
        } finally{
            logService.saveLog(log);
        }
        return null;
    }
}

(3).applicationContext.xml

<aop:config>
        <aop:aspect id="loggerAspect" ref="logger">
            <aop:around method="record" pointcut="(execution(* com.aijava.distributed.ssh.service..*.add*(..))
                                              or   execution(* com.aijava.distributed.ssh.service..*.update*(..))
                                              or   execution(* com.aijava.distributed.ssh.service..*.delete*(..)))
                                            and !bean(logService)"/>
        </aop:aspect>
</aop:config>

注意切入点表达式,!bean(logService) 做日志通知的时候,不要给日志本身做日志,否则会造成无限循环!

有关更详细的Spring AOP知识,可以查看Spring官方文档第9章Aspect Oriented Programming with Spring 

7.JDK动态代理介绍

例子:

(1)UserService.java

public interface UserService {
    
    public void add();
}

(2)UserServiceImpl.java

public class UserServiceImpl implements UserService{

    public void add() {
        System.out.println("User add()...");
    }
    
}

(3)ProxyUtils.java

public class ProxyUtils implements InvocationHandler{
    
    private Object target;
    
    public ProxyUtils(Object target){
        this.target = target;
    }
    
    public Object getTarget() {
        return target;
    }
    
    public void setTarget(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("do sth before...");
        method.invoke(target, args);
        System.out.println("do sth after...");
        return null;
    }

}

(4)Test.java

public class Test {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyUtils proxyUtils = new ProxyUtils(userService);
        UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),UserServiceImpl.class.getInterfaces(), proxyUtils);
        proxyObject.add();
    }
}

JDK动态代理核心还是一个InvocationHandler,记住这个就行了。

 

本文作者:LiuRuoWang 邮箱:1204141617@qq.com

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155310.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 编译树莓派Linux内核[通俗易懂]

    编译树莓派Linux内核[通俗易懂]  RaspberryPi内核Linux代码存储在GitHub中,可以在github.com/raspberrypi/linux上查看。一、下载linux内核源码gitclone–depth=1https://github.com/raspberrypi/linux  上面的命令将下载当前的活动分支。省略–depth=1将下载整个存储库,包括所有分支的完整历史记录,但占用更多的存储空间。要下载不同的分支,可以使用以下–branch选项:gitclone–depth=1–b

    2022年7月23日
    6
  • 这可能是目前最全的Redis高可用技术解决方案总结

    这可能是目前最全的Redis高可用技术解决方案总结

    2021年11月5日
    170
  • 程序测试与调试

    程序测试与调试

    2021年11月18日
    52
  • unzip解压命令出现错误_软件解压不了怎么办

    unzip解压命令出现错误_软件解压不了怎么办寻找gzip获取一个网页数据返回的编码类型是gzip,我该怎么解压缩HTTP头获取?如何用vb获取网络上的xml文件,并解析内容关于GZIP的解码怎样解gzip的压缩请问Wininet是否可以进行Gzip的http传输,尤其是post的时候!如果可以如何实现?关于GZIP格式解压缩HTTP数据包的问题,我使用ZLIB为什么必须先保存文件,内存解压缩出错

    2022年9月6日
    2
  • SPI的原理_托里拆利实验原理讲解

    SPI的原理_托里拆利实验原理讲解什么是SPISPI是英语SerialPeripheralinterface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字…

    2022年10月15日
    0
  • 带通滤波器的主要功能和作用_带通滤波器的作用是什么

    带通滤波器的主要功能和作用_带通滤波器的作用是什么带通滤波器的工作原理与应用带通滤波器可以理解成为一个电子接口单元,这个单元可以将特定频率范围内的信号传输过去,而阻断这个频率范围以外的信号,达到选择性传输的目的。与此对应,滤波器可以分为低通滤波器,即某频率以下的信号可以传输过去。高通滤波器和带阻滤波器。这些功能都是通过特定电子原件按照不同的布置实现的。比如电容串联可以阻止低频率信号,导通高频率信号。而并联一个电容就可以实现将高频信号短路的功能。又…

    2022年9月5日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号