Spring面向切面(AOP)

  • AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志、事务、权限等,Struts2的拦截器设计就是基于AOP的思想。
  • AOP的基本概念
    • Aspect(切面):通常是一个类,里面可以定义切入点和通知
    • JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。
    • Advice(通知):AOP在特定的切入点上执行的增强处理,有before、after、afterReturning、afterThrowing、around
    • Pointcut(切入点):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。
  • Spring AOP
    • Spring中的AOP代理离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是有IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLID代理。
  • 基于注解的AOP配置方式
    • 启用@Asject支持

      • 在applicationContext.xml中配置

        <aop:aspectj-autoproxy />
    • 通知类型介绍
      • Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可。
      • AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值。
      • AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以用过改形参名来访问目标方法中所抛出的异常对象。
      • After:在目标方法完成之后做增强,无论目标方法什么时候成功完成。@After可以指定一个切入点表达式。
      • Around:环绕通知,在目标方法完成前后做增强处理,环绕通知最重要的通知类型,像事务、日志等都是环绕通知。
  • 测试实例
    • Operator.java --> 切面类

      @Component
      @Aspect
      public class Operator {
      
          @Pointcut("execution(* com.lynn.learning.spring.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);
          }
      
          @Around("pointCut()")
          public Object around(ProceedingJoinPoint pjp){
              Object obj = null;
              System.out.println("AOP Around before...");
              try {
                  obj = pjp.proceed();
              } catch (Throwable e) {
                  e.printStackTrace();
              }
              System.out.println("AOP Around after...");
              return obj;
          }
      }
    • UserService.java --> 定义一些目标方法

      @Service
      public class UserService {
      
          public void add(){
              System.out.println("UserService add()");
          }
      
          public String delete(){
              System.out.println("UserService delete()");
              return "delete函数返回值";
          }
      
          public void edit(){
              System.out.println("UserService edit()");
              int i = 5/0;
          }
      }
    • applicationContext.xml

      <context:component-scan base-package="com.lynn.learning.spring"/>
      <aop:aspectj-autoproxy />
    • UserServiceTest.java

      public class UserServiceTest {
      
          @Test
          public void add() {
              ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
              UserService userService = (UserService) ctx.getBean("userService");
              userService.add();
          }
      
          @Test
          public void delete() {
              ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
              UserService userService = (UserService) ctx.getBean("userService");
              userService.delete();
          }
      
          @Test
          public void edit() {
              ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
              UserService userService = (UserService) ctx.getBean("userService");
              userService.edit();
          }
      }
    • 注意:做环绕通知的时候,调用ProceedingJoinPoint的proceed()方法才会执行目标方法,同时需要返回值,否则在AfterReturning中无法获取返回值。
  • 通知执行的优先级
    • 进入目标方法时,先织入Around,再织入Before
    • 退出目标方法时,织入Around,再织入AfterReturning,最后才织入After
  • 切入点的定义和表达式
    • 切入点表达式的定义算是整个AOP中的核心,有一套自己的规范
    • Spring AOP支持的切入点指示符:
      • @Pointcut("execution(* com.lynn.learning.spring.service..*.*(..))")

        • 第一个*表示匹配任意的方法返回值,..(两个点)表示零个或多个,上面的第一个..表示service包及其子包,第二个*表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数
        • execution:用来匹配执行方法的连接点
      • @Pointcut("within(com.lynn.learning.spring.service*)")
        • within限定匹配方法的连接点,上面的就是表示匹配service包下的任意连接点
      • @Pointcut("this(com.lynn.learning.spring.service.UserService)")
        • this用来限定AOP代理必须是指定类型的实例,如上,指定了一个特定的实例,就是UserService
      • @Pointcut("bean(userService)")
        • bean也是非常常用的,bean可以指定IOC容器中的bean的名称。
  • Spring实现自定义注解
    • 创建自定义注解

      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Inherited
      public @interface MyLog {
          String requestUrl();
      }
    • 解析注解,通过AOP实现

      @Component
      @Aspect
      public class MyLogAspect {
      
          @Pointcut(value = "@annotation(com.lynn.learning.spring.annotation.MyLog)")
          public void pointcut() {}
      
          @Around(value = "pointcut() && @annotation(myLog)")
          public Object around(ProceedingJoinPoint point, MyLog myLog) {
              System.out.println("+++++执行了around方法+++++");
              String requestUrl = myLog.requestUrl();
              Class clazz = point.getTarget().getClass();
              Method method = ((MethodSignature) point.getSignature()).getMethod();
              System.out.println("执行了类:" + clazz + ", 方法:" + method + " 自定义请求地址:" + requestUrl);
              try {
                  return point.proceed();
              } catch (Throwable throwable) {
                  return throwable.getMessage();
              }
          }
      
          @AfterReturning(value = "pointcut() && @annotation(myLog)", returning = "result")
          public Object afterReturning(JoinPoint joinPoint, MyLog myLog, Object result) {
              System.out.println("+++++执行了afterReturning方法+++++");
              System.out.println("执行结果:" + result);
              return result;
          }
      
          @AfterThrowing(value = "pointcut() && @annotation(myLog)", throwing = "ex")
          public void afterThrowing(JoinPoint joinPoint, MyLog myLog, Exception ex) {
              System.out.println("+++++执行了afterThrowing方法+++++");
              System.out.println("请求:" + myLog.requestUrl() + " 出现异常");
          }
      }
    • 使用自定义注解

      @Service
      public class UserService {
      
          @MyLog(requestUrl = "add")
          public void add(){
              System.out.println("UserService add()");
          }
      
          public String delete(){
              System.out.println("UserService delete()");
              return "delete函数返回值";
          }
      
          public void edit(){
              System.out.println("UserService edit()");
              int i = 5/0;
          }
      }
    • 测试

      public class UserServiceTest {
      
          @Test
          public void add() {
              ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
              UserService userService = (UserService) ctx.getBean("userService");
              userService.add();
          }
      
      }
    • 打印结果

      +++++执行了around方法+++++
      执行了类:class com.lynn.learning.spring.service.UserService, 方法:public void com.lynn.learning.spring.service.UserService.add() 自定义请求地址:add
      AOP Around before...
      AOP Before Advice...
      UserService add()
      AOP Around after...
      AOP After Advice...
      AOP AfterReturning Advice:null
      +++++执行了afterReturning方法+++++
      执行结果:null
    • @Retention:元注解,有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型。
      • CLASS:表示注解的信息被保留在class文件(字节码文件)。当程序编译时,不会被虚拟机保存在运行时。默认行为。
      • SOURCE:表示注解的信息会被编辑器抛弃,不会保留在class文件中,注解的信息只会保存在源文件中
      • RUNTIME:表示注解的信息会被保留在class文件(字节码文件)中,当程序编译时,会被虚拟机保存在运行时。所以他们可以使用反射的方式读取。Retention.RUNTIME可以从JVM中读取Annotation注解的信息,以便在分析程序的时候使用。
    • @Target:用来修饰注解的元注解。属性ElementType也是一个枚举类型
      • TYPE:用于描述类、接口(包括注解类型)或enum
      • FIELD:用于描述域
      • METHOD:用于描述方法
      • PARAMETER:用于描述参数(参数名)
      • CONSTRUCTOR:用于描述构造函数
      • LOCAL_VARIABLE:用于描述局部变量
      • ANNOTATION_TYPE:用于描述注解
      • PACKAGE:用于描述包
      • TYPE_PARAMETER:用于描述参数类型
      • TYPE_USE:任何位置都可以
    • @Documented:表明这个注解应该被javadoc工具记录。默认情况下javadoc是不包括注解的,但如果声明注解是指定了@Documented,则他会被javadoc之类的工具处理,所以注解类型信息也会被包括在生成的文档中。
    • @Inherited:指明被注解的类会自动继承. 更具体地说,如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.

原文地址:https://www.cnblogs.com/lynn16/p/10687106.html

时间: 2024-10-12 07:48:43

Spring面向切面(AOP)的相关文章

spring面向切面aop拦截器

spring中有很多概念和名词,其中有一些名字不同,但是从功能上来看总感觉是那么的相似,比如过滤器.拦截器.aop等. 过滤器filter.spring mvc拦截器Interceptor .面向切面编程aop,实际上都具有一定的拦截作用,都是拦截住某一个面,然后进行一定的处理. 在这里主要想着手的是aop,至于他们的比较,我想等三个都一一了解完了再说,因此这里便不做过多的比较. 在我目前的项目实践中,只在一个地方手动显示的使用了aop,那便是日志管理中对部分重要操作的记录. 据我目前所知,ao

Spring面向切面编程(二)简单AOP实例

简单实现一个用户登陆的功能,在用户登陆之前进行日志打印,用户登陆之后进行登陆成功日志打印. Maven添加Spring jar spring面向切面编程(一)AOP术语 添加Spring AOP的jar 参考:Maven添加Spring jar 还需添加: 创建User类: package com.user; public class User { private String username; private String password; public String getUsernam

Spring框架使用(控制反转,依赖注入,面向切面AOP)

参见:http://blog.csdn.net/fei641327936/article/details/52015121 Mybatis: 实现IOC的轻量级的一个Bean的容器 Inversion of control 控制反转:由容器控制程序之间的关系,不是程序代码操作 Depend Inject 依赖注入 Aspect oriented programming 面向切面编程 Spring能帮助我们根据配置文件创建及组装对象之间的依赖关系: Spring面向切面编程能帮助我们无耦合的实现日

Spring 面向切面编程(AOP)

Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Control – IOC) 理解依赖注入(DI – Dependency Injection) Bean XML 配置(1)- 通过XML配置加载Bean Bean XML 配置(2)- Bean作用域与生命周期回调方法配置 Bean XML 配置(3)- 依赖注入配置 Bean XML 配置(

Spring面向切面编程

  1.面向切面编程(AOP)的概念:把项目中需要在多处用到的功能,比如日志.安全和事物等集中到一个类中处理,而不用在每个需要用到该功能的地方显式调用.   2.术语解释:        横切关注点:分布应用于多处的功能        切面:横切关注点可以被模块化为一个类,这个类被称为一个切面        通知(advice):切面要完成的工作.Spring的通知有5种类型:before.after.after-returning.after-throwing和around这五种类型.    

Spring面向切面 --- AspectJ --- 简单使用

Spring面向切面 --- AspectJ --- 简单使用 昨天回复说说的时候突然写下了下面的一段话:分享一下: <!--******************************************* 其实经过的记忆是可以进行道德化的篡改的,就像夏目漱石的<我是猫>:但是不管怎么改,真正的事实是由每一个人的碎片拼起来的:经济学里计算成本的是在计算将来的成本而不是过去的成本,就像动漫<未来日记>一样:过去发生的事情总是在影响着将来,但是过去发生的事情却不能充当将来下

Spring面向切面编程(AOP)

1 spring容器中bean特性 Spring容器的javabean对象默认是单例的. 通过在xml文件中,配置可以使用某些对象为多列. Spring容器中的javabean对象默认是立即加载(立即实例化:spring加载完成,立即创建对象) scope:属性 singleton:默认值为单例,默认也是立即加载,在加载完成spring容器的时候,bean对象已经创建完成 prototype:多例的,默认懒加载,spring容器加载完成的时候,不会创建bean的对象,只有从容器获得bean对象的

Spring学习1_面向切面( AOP )实现原理

面向切面编程 (Aspect Oriented Programming,简称AOP) 是Spring的一个重要特性,其原理是采用动态代理方式实现. 下面通过一个Demo来模拟AOP实现 整个代码目录结构如下: 其中LogInterceptor类完成为所有Service方法添加日志记录的功能. 1.Dao层实现 package com.dao; public class UserDaoImpl implements UserDao { @Override public void save() {

详解Spring面向切面编程(AOP)三种实现

一.什么是AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP引入封装.继承.多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合.不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能.日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性.异常处理和透明的持续性也都是如此,这