springAOP

springAOP

前言:
  • 静态代理
    静态代理类:
    1、原则:和目标方法类功能一致且和目标类实现同样的接口。
    2、缺点:

    • 往往在开发我们自己写的不仅仅是一个业务,两个业务,而我们的业务会有很多,如果每一个业务开发一个静态代理类,不仅没有减少我们的工作量,而且让我们的工作量多了很多。
    • 如何解决这个问题?能不能为我们现有的业务层动态的创建代理类,通过动态代理去解决我们现有的业务层中的业务代码冗余问题。
  • 动态代理

    通过jdk提供的Proxy类,动态为现有的业务生成代理类。

代码示例:

/**
 * JDK中的动态代理
 * @author MOTUI
 *
 */
public class TestUserServiceImplDynamicProxy {
    public static void main(String[] args) {
        final UserService userService = new UserServiceImpl();
        //使用JDK提供的Proxy对象,用来产生动态代理类
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] interfaces = {UserService.class};
        InvocationHandler h = new InvocationHandler() {
            //参数1:生成的代理对象
            //参数2:当前调用的方法对象
            //参数3:当前带哦啊用方法参数
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                try {
                    System.out.println("开启事务");
                    //调用真正的业务逻辑
                    Object invoke = method.invoke(userService, args);
                    System.out.println("提交事务");
                    return invoke;
                } catch (Exception e) {
                    System.out.println("回滚事务");
                    e.printStackTrace();
                }
                return null;
            }
        };
        //参数1:当前线程的类加载器   加载.class文件
        //参数2:生成代理的对象的目标类的接口类型
        //参数3:当通过代理对象去调方法时,会优先进入InvocationHander类的invoke方法
        UserService userServiceProxy =  (UserService) Proxy.newProxyInstance(loader, interfaces, h);//返回静态代理对象
        //userServiceProxy.save();
        String query = userServiceProxy.query("小王");
        System.out.println(query);
    }
}

学习AOP需要知道几个重要的概念:

  • 通知(Advice):处理目标方法以外的操作都称之为通知
  • 切入点(PointCut):要为哪些类中的哪些方法加入通知
  • 切面(Aspect): 通知 + 切入点

    • 前置通知:
      代码:

      public class RecordMethodNameBeforeAdvice implements MethodBeforeAdvice {
      
          /**
           * 参数1:当前目标类中调用的方法对象
           * 参数2:当前目标类中调用的方法的参数
           * 参数3:目标对象
           */
          @Override
          public void before(Method method, Object[] args, Object target)
                  throws Throwable {
              System.out.println("当前执行的方法:"+method.getName());
          }
      
      }

      配置文件:

          <!-- 管理Bean -->
          <bean id="userService" class="com.motui.first.UserServiceImpl"/>
          <!-- 配置通知 -->
          <bean id="methodBeforeAdvice" class="com.motui.first.RecordMethodNameBeforeAdvice"></bean>
          <!-- 配置切面 -->
          <aop:config>
              <!-- 配置切入点 
              expression:指定哪个类的那个方法需要加入通知-->
              <aop:pointcut expression="execution(* com.motui.first.UserServiceImpl.*(..))" id="pc"/>
              <!-- 组合切入点和通知 -->
              <!-- 
                  advice-ref:告诉当前切面使用的是哪个通知 书写通知bean的id
                  pointcut-ref:当前通知加入哪个切入点
               -->
              <aop:advisor advice-ref="methodBeforeAdvice" pointcut-ref="pc"/>
          </aop:config>
    • 后置通知:
      代码:

      public class MyAfter implements AfterReturningAdvice ,ThrowsAdvice {
      //参数1: 目标类中当前执行的方法的返回值
      //参数2: 目标类中当前执行的方法对象
      //参数3: 目标类中当前执行的方法的参数
      //参数4: 目标对象
      @Override
      public void afterReturning(Object result, Method m, Object[] args,
              Object traget) throws Throwable {
          System.out.println("返回值:"+result);
          System.out.println("当前目标类的目标方法"+m.getName());
          System.out.print("参数列表:");
          if (args.length>0) {
              for (Object object : args) {
                  System.out.println(object);
              }
          }else{
              System.out.println("参数为空");
          }
          System.out.println("目标:"+traget);
      }
      
      //出现异常时进入的通知方法
      public void afterThrowing(Method method, Object[] args, Object target,Exception ex){
          System.out.println("异常通知的方法的名称: "+method.getName());
          System.out.println("异常通知的方法的参数: "+args);
          System.out.println("异常通知的目标对象"+target);
          System.out.println("异常通知的异常的信息 :" +ex.getMessage());
      }
      }

      配置文件:

      <!-- 管理bean -->             
      <bean id="deptService" class="com.motui.after.DeptServiceImpl"/>
      <bean id="myAfter" class="com.motui.after.MyAfter"/>
      <!-- 配置切入点 -->
      <aop:config>
      <aop:pointcut expression="execution(* com.motui.after.DeptServiceImpl.*(..))" id="pc"/>
      <aop:advisor advice-ref="myAfter" pointcut-ref="pc"/>
      </aop:config>
    • 环绕通知:
      代码:

      /**
       * 记录运行时长
       * @author MOTUI
       *
       */
      public class TestAroundAOP implements MethodInterceptor{
      
          @Override
          public Object invoke(MethodInvocation mi) throws Throwable {
              System.out.println("记录运行时长");
              long start = new Date().getTime();
              Object proceed = mi.proceed();//返回值为目标方法的返回值,这个返回值在通知类中返回
              long end = new Date().getTime();
              System.out.println("运行时长:"+(end-start));
              return proceed;
          }
      
      }

      配置文件:

          <!-- 管理Service -->
      <bean id="addressService" class="com.motui.around.AddressServiceImpl"/>
      
      <bean id="testAroundAOP" class="com.motui.around.TestAroundAOP"/>
      <!-- 配置切入点 -->
      <aop:config>
          <aop:pointcut expression="execution(* com.motui.around.AddressServiceImpl.*(..))" id="pc"/>
          <aop:advisor advice-ref="testAroundAOP" pointcut-ref="pc"/>
      </aop:config>
    • 异常通知:

      代码在后置通知中。

    • execution表达式:

      execution(返回值类型 包名.类名.方法名(参数列表))
      示例:
              execution(* com.motui.service.impl.UserServiceImpl.save(..))
                  返回值:任意
                  包:com.motui.service.impl
                  类:UserServiceImpl
                  方法:save
                  参数:任意
      
              execution(* com.motui.service.impl.UserServiceImpl.*(..))
                  返回值:任意
                  包:com.motui.service.impl
                  类:UserServiceImpl
                  方法:任意方法
                  参数:任意
      

在spring中有两种代理模式(JDK中的proxy和CGLIB):
在spring配置文件中可以通过proxy-target-class属性指定,true是CGLIB代理;false是Proxy代理

总结:

    Spring的核心思想
    IOC:控制反转    DI:依赖注入
    AOP:面向切面的编程思想
            AOP:通过动态为现有项目中的目标类创建代理对象,解决项目中的一些通用问题。
            AOP几个重要概念:
            通知(Advice):除了目标方法以外的操作称之为通知
            切入点(Pointcut):项目中的哪些类的哪些方法需要加入通知
            切面(Aspect):通知 + 切入点
    AOP的底层实现
            1、JDK的动态代理  Proxy   基于接口生成的代理
            2、Spring CGBIL (CGLIB用来生成动态代理) 基于实现类生成的代理
            3、JDK Proxy和CGLIB生成代理的区别?
                Proxy根据接口生成动态代理对象,产生对象的返回值是接口类型
                CGLIB根据实现类生成代理对象,产生对象的返回值是实现类类型(目标类型)
###Spring AOP 的概念 AOP(Aspect-Oriented Programming)即面向切面编程,是一种编程范式,旨在通过分离横切关注点来提高模块化程度。在 Spring 框架中,AOP 被广泛用于实现诸如日志记录、事务管理、安全性等通用功能,这些功能通常与业务逻辑无关但又需要在多个地方重复使用。 Spring AOP 主要是基于 AspectJ 实现的,尽管 AspectJ 是一个独立的 AOP 框架,并不是 Spring 的组成部分,但它通常与 Spring 一起使用以提供更强大的 AOP 功能[^1]。Spring AOP 支持两种方式来定义切面:基于 XML 配置文件的方式基于注解的方式。 ###Spring AOP 的原理 Spring AOP 使用运行时代理来实现 AOP 功能,这意味着它会在运行时动态生成代理对象。对于实现了接口的类,Spring AOP 默认使用 JDK 动态代理;而对于没有实现接口的类,则会使用 CGLIB 代理[^4]。这种方式允许在不修改原始代码的情况下向程序中添加新的行为。 织入(Weaving)是将增强(advice)应用到目标对象的过程,Spring AOP 在运行时进行织入操作[^3]。当创建了代理对象后,所有对目标对象方法的调用都会被拦截,并且可以插入额外的操作,比如在方法执行前后做一些处理。 ###Spring AOP 的使用教程 要开始使用 Spring AOP,首先需要确保项目中包含了必要的依赖。如果使用 Maven 构建工具,可以在 `pom.xml` 文件中加入如下依赖: ```xml <!-- 引入aop依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 一旦添加了依赖并刷新了 Maven 项目,就可以开始编写切面了。下面是一个简单的例子,展示如何使用注解来定义一个切面: ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("Method " + joinPoint.getSignature().getName() + " is called."); } } ``` 在这个示例中,`LoggingAspect` 类被标记为 `@Aspect` `@Component` 注解,这样 Spring 就能识别这是一个切面组件。`@Before` 注解指定了在哪些方法上应用前置通知(before advice),这里的表达式表示匹配 `com.example.service` 包下所有的方法。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值