热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解Spring学习总结——Spring实现AOP的多种方式

这篇文章主要介绍了详解Spring学习总结——Spring实现AOP的多种方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

目录

一、基于XML配置的Spring AOP

二、使用注解配置AOP

三、AspectJ切点函数

四、AspectJ通知注解

五、零配置实现Spring IoC与AOP

AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术。AOP是OOP的补充,是spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高。动态织入则不需要改变目标模块。Spring框架实现了AOP,使用注解配置完成AOP比使用XML配置要更加方便与直观。

一、基于XML配置的Spring AOP

在讲注解实现AOP功能前先用前面学习过的使用xml配置Spring AOP功能,这样是为了对比以便更好的理解。

1.1、新建一个Maven项目,添加引用,项目的pom.xml文件如下:


 4.0.0

 com.zhangguo
 Spring052
 0.0.1-SNAPSHOT
 jar

 Spring052
 http://maven.apache.org

 
  UTF-8
  4.3.0.RELEASE
 
 
  
   junit
   junit
   test
   4.10
  
  
   org.springframework
   spring-context
   ${spring.version}
  
  
   org.aspectj
   aspectjweaver
   1.8.9
  
  
   cglib
   cglib
   3.2.4
  
 


1.2、创建要被代理的Math类,代码如下:

package com.zhangguo.Spring052.aop01;

/**
 * 被代理的目标类
 */
public class Math{
 //加
 public int add(int n1,int n2){
  int result=n1+n2;
  System.out.println(n1+"+"+n2+"="+result);
  return result;
 }
 
 //减
 public int sub(int n1,int n2){
  int result=n1-n2;
  System.out.println(n1+"-"+n2+"="+result);
  return result;
 }
 
 //乘
 public int mut(int n1,int n2){
  int result=n1*n2;
  System.out.println(n1+"X"+n2+"="+result);
  return result;
 }
 
 //除
 public int div(int n1,int n2){
  int result=n1/n2;
  System.out.println(n1+"/"+n2+"="+result);
  return result;
 }
}

1.3、编辑AOP中需要使用到的通知类Advices.Java代码如下:

package com.zhangguo.Spring052.aop01;

import org.aspectj.lang.JoinPoint;

/**
 * 通知类,横切逻辑
 *
 */
public class Advices {
 
 public void before(JoinPoint jp){
  System.out.println("----------前置通知----------");
  System.out.println(jp.getSignature().getName());
 }
 
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
}

1.4、配置容器初始化时需要的XML文件,aop01.xml文件内容如下:

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  
 
 
 
 
 
 
 
 
  
  
   
   
   
   
   
  
 



1.5、测试代码Test.java如下:

package com.zhangguo.Spring052.aop01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

 public static void main(String[] args) {
  ApplicationContext ctx = new ClassPathXmlApplicationContext("aop01.xml");
  Math math = ctx.getBean("math", Math.class);
  int n1 = 100, n2 = 5;
  math.add(n1, n2);
  math.sub(n1, n2);
  math.mut(n1, n2);
  math.div(n1, n2);
 }

}

运行结果:

二、使用注解配置AOP

2.1、在上一个示例中修改被代理的类Math,为了实现IOC扫描在Math类上注解了@Service并命名bean为math。相当于上一个示例中在xml配置文件中增加了一个bean,,Math类的代码如下:

package com.zhangguo.Spring052.aop02;

import org.springframework.stereotype.Service;

/**
 * 被代理的目标类
 */
@Service("math")
public class Math{
 //加
 public int add(int n1,int n2){
  int result=n1+n2;
  System.out.println(n1+"+"+n2+"="+result);
  return result;
 }
 
 //减
 public int sub(int n1,int n2){
  int result=n1-n2;
  System.out.println(n1+"-"+n2+"="+result);
  return result;
 }
 
 //乘
 public int mut(int n1,int n2){
  int result=n1*n2;
  System.out.println(n1+"X"+n2+"="+result);
  return result;
 }
 
 //除
 public int div(int n1,int n2){
  int result=n1/n2;
  System.out.println(n1+"/"+n2+"="+result);
  return result;
 }
}

 2.2、修改通知类Advices,代码中有3个注解,@Component表示该类的实例会被Spring IOC容器管理;@Aspect表示声明一个切面;@Before表示before为前置通知,通过参数execution声明一个切点,Advices.java代码如下所示:

package com.zhangguo.Spring052.aop02;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 *
 */
@Component
@Aspect
public class Advices {
 @Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
 public void before(JoinPoint jp){
  System.out.println("----------前置通知----------");
  System.out.println(jp.getSignature().getName());
 }
 
 @After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
}

 上面的代码与下面的配置基本等同

 
 
 
 
 
  
  
   
   
   
   
   
  
 

2.3、新增配置文件aop02.xml,在配置IOC的基础上增加了aop:aspectj-autoproxy节点,Spring框架会自动为与AspectJ切面配置的Bean创建代理,proxy-target-class="true"属性表示被代理的目标对象是一个类,而非实现了接口的类,主要是为了选择不同的代理方式。

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  
  
  


2.4、测试运行代码Test.java如下:

package com.zhangguo.Spring052.aop02;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

 public static void main(String[] args) {
  ApplicationContext ctx = new ClassPathXmlApplicationContext("aop02.xml");
  Math math = ctx.getBean("math", Math.class);
  int n1 = 100, n2 = 5;
  math.add(n1, n2);
  math.sub(n1, n2);
  math.mut(n1, n2);
  math.div(n1, n2);
 }

}

运行结果:

三、AspectJ切点函数

切点函数可以定位到准确的横切逻辑位置,在前面的示例中我们只使用过execution(* com.zhangguo.Spring052.aop02.Math.*(..)),execution就是一个切点函数,但该函数只什么方法一级,如果我们要织入的范围是类或某个注解则execution就不那么好用了,其实一共有9个切点函数,有不同的针对性。

@AspectJ使用AspectJ专门的切点表达式描述切面,Spring所支持的AspectJ表达式可分为四类:

方法切点函数:通过描述目标类方法信息定义连接点。

方法参数切点函数:通过描述目标类方法入参信息定义连接点。

目标类切点函数:通过描述目标类类型信息定义连接点。

代理类切点函数:通过描述代理类信息定义连接点。

常见的AspectJ表达式函数:

  • execution():满足匹配模式字符串的所有目标类方法的连接点
  • @annotation():任何标注了指定注解的目标方法链接点
  • args():目标类方法运行时参数的类型指定连接点
  • @args():目标类方法参数中是否有指定特定注解的连接点
  • within():匹配指定的包的所有连接点
  • target():匹配指定目标类的所有方法
  • @within():匹配目标对象拥有指定注解的类的所有方法
  • @target():匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
  • this():匹配当前AOP代理对象类型的所有执行方法

最常用的是:execution(<修饰符模式>&#63;<返回类型模式><方法名模式>(<参数模式>)<异常模式>&#63;)切点函数,可以满足多数需求。

为了展示各切点函数的功能现在新增一个类StrUtil,类如下:

package com.zhangguo.Spring052.aop03;

import org.springframework.stereotype.Component;

@Component("strUtil")
public class StrUtil {
 public void show(){
  System.out.println("Hello StrUtil!");
 }
}

测试代码如下:

package com.zhangguo.Spring052.aop03;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

 public static void main(String[] args) {
  ApplicationContext ctx = new ClassPathXmlApplicationContext("aop03.xml");
  IMath math = ctx.getBean("math", Math.class);
  int n1 = 100, n2 = 5;
  math.add(n1, n2);
  math.sub(n1, n2);
  math.mut(n1, n2);
  math.div(n1, n2);
  
  StrUtil strUtil=ctx.getBean("strUtil",StrUtil.class);
  strUtil.show();
 }

}

3.1、切点函数execution,通知与切面的定义如下:

package com.zhangguo.Spring052.aop03;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 *
 */
@Component
@Aspect
public class Advices {
 @Before("execution(* com.zhangguo.Spring052.aop03.Math.*(..))")
 public void before(JoinPoint jp){
  System.out.println("----------前置通知----------");
  System.out.println(jp.getSignature().getName());
 }
 
 //execution切点函数
 //com.zhangguo.Spring052.aop03包下所有类的所有方法被切入
 @After("execution(* com.zhangguo.Spring052.aop03.*.*(..))")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
}

运行结果如下:

execution(<修饰符模式>&#63;<返回类型模式><方法名模式>(<参数模式>)<异常模式>&#63;)

3.2、切点函数within

 //within切点函数
 //com.zhangguo.Spring052.aop03包下所有类的所有方法被切入
 @After("within(com.zhangguo.Spring052.aop03.*)")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }

3.3、this切点函数

 //this切点函数
 //实现了IMath接口的代理对象的任意连接点
 @After("this(com.zhangguo.Spring052.aop03.IMath)")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }

3.4、args切点函数

 //args切点函数
 //要求方法有两个int类型的参考才会被织入横切逻辑
 @After("args(int,int)")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }

如果参数类型不是基本数据类型则需要包名。

3.5、@annotation切点函数

先自定义一个可以注解在方法上的注解

package com.zhangguo.Spring052.aop03;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnno {
}


 //@annotation切点函数
 //要求方法必须被注解com.zhangguo.Spring052.aop03.MyAnno才会被织入横切逻辑
 @After("@annotation(com.zhangguo.Spring052.aop03.MyAnno)")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }

package com.zhangguo.Spring052.aop03;

import org.springframework.stereotype.Component;

@Component("strUtil")
public class StrUtil {
 @MyAnno
 public void show(){
  System.out.println("Hello StrUtil!");
 }
}

运行结果:

其它带@的切点函数都是针对注解的

四、AspectJ通知注解

AspectJ通知注解共有6个,常用5个,引介少用一些。

先解决定义切点复用的问题,如下代码所示,切点函数的内容完全一样:

package com.zhangguo.Spring052.aop04;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 *
 */
@Component
@Aspect
public class Advices {
 @Before("execution(* com.zhangguo.Spring052.aop04.Math.*(..))")
 public void before(JoinPoint jp){
  System.out.println("----------前置通知----------");
  System.out.println(jp.getSignature().getName());
 }
 
 @After("execution(* com.zhangguo.Spring052.aop04.Math.*(..))")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
}

可以先定义一个切点然后复用,如下所示:

package com.zhangguo.Spring052.aop04;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 */
@Component
@Aspect
public class Advices {
 //切点
 @Pointcut("execution(* com.zhangguo.Spring052.aop04.Math.*(..))")
 public void pointcut(){
 }
 
 @Before("pointcut()")
 public void before(JoinPoint jp){
  System.out.println("----------前置通知----------");
  System.out.println(jp.getSignature().getName());
 }
 
 @After("pointcut()")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
}

修改Advices.java文件,增加各种通知类型如下:

package com.zhangguo.Spring052.aop04;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 通知类,横切逻辑
 */
@Component
@Aspect
public class Advices {
 //切点
 @Pointcut("execution(* com.zhangguo.Spring052.aop04.Math.a*(..))")
 public void pointcut(){
 }
 
 //前置通知
 @Before("pointcut()")
 public void before(JoinPoint jp){
  System.out.println(jp.getSignature().getName());
  System.out.println("----------前置通知----------");
 }
 
 //最终通知
 @After("pointcut()")
 public void after(JoinPoint jp){
  System.out.println("----------最终通知----------");
 }
 
 //环绕通知
 @Around("execution(* com.zhangguo.Spring052.aop04.Math.s*(..))")
 public Object around(ProceedingJoinPoint pjp) throws Throwable{
  System.out.println(pjp.getSignature().getName());
  System.out.println("----------环绕前置----------");
  Object result=pjp.proceed();
  System.out.println("----------环绕后置----------");
  return result;
 }
 
 //返回结果通知
 @AfterReturning(pointcut="execution(* com.zhangguo.Spring052.aop04.Math.m*(..))",returning="result")
 public void afterReturning(JoinPoint jp,Object result){
  System.out.println(jp.getSignature().getName());
  System.out.println("结果是:"+result);
  System.out.println("----------返回结果----------");
 }
 
 //异常后通知
 @AfterThrowing(pointcut="execution(* com.zhangguo.Spring052.aop04.Math.d*(..))",throwing="exp")
 public void afterThrowing(JoinPoint jp,Exception exp){
  System.out.println(jp.getSignature().getName());
  System.out.println("异常消息:"+exp.getMessage());
  System.out.println("----------异常通知----------");
 }
}

运行结果:

五、零配置实现Spring IoC与AOP

为了实现零配置在原有示例的基础上我们新增一个类User,如下所示:

package com.zhangguo.Spring052.aop05;

public class User {
 public void show(){
  System.out.println("一个用户对象");
 }
}

该类并未注解,容器不会自动管理。因为没有xml配置文件,则使用一个作为配置信息,ApplicationCfg.java文件如下:

package com.zhangguo.Spring052.aop05;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration //用于表示当前类为容器的配置类,类似
@ComponentScan(basePackages="com.zhangguo.Spring052.aop05") //扫描的范围,相当于xml配置的结点
@EnableAspectJAutoProxy(proxyTargetClass=true) //自动代理,相当于
public class ApplicationCfg {
 //在配置中声明一个bean,相当于
 @Bean
 public User getUser(){
  return new User();
 }
}

该类的每一部分内容基本都与xml 配置有一对一的关系,请看注释,这样做要比写xml方便,但不便发布后修改。测试代码如下:

package com.zhangguo.Spring052.aop05;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

 public static void main(String[] args) {
  // 通过类初始化容器
  ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationCfg.class);
  Math math = ctx.getBean("math", Math.class);
  int n1 = 100, n2 = 0;
  math.add(n1, n2);
  math.sub(n1, n2);
  math.mut(n1, n2);
  try {
   math.div(n1, n2);
  } catch (Exception e) {
  }
  
  User user=ctx.getBean("getUser",User.class);
  user.show();
 }

}

 advices.java 同上,没有任何变化,运行结果如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 标题: ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • docker增加restart=always, docker重启后自动启动容器的方法
    本文介绍了在运行docker容器时如何添加参数来保证每次docker服务重启后容器也自动重启的方法,以及如何使用命令来更新已启动的容器。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
author-avatar
郭绍玲刚珍雅瑜_658
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有