Spring 面向切面编程

AOP 代理设置模式

概述:

  • 所谓代理,就是一个人或某机构代表一个人或某机构采取行动。在一些情况下,客户不想或不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中间的作用。
  • 通俗讲:
    - 经纪人和艺人的关系,经纪人便是代理,艺人就是目标用户,艺人不想做的(安装设备,舞台等),由经纪人去联系处理,这样的模式就是代理模式。

JDK: 静态代理 动态代理 cglib 生成的动态代理

  • 静态代理
    - 缺点:扩展性差、每次生成新的目标对象类,即使一样,也要重新生成一个代理对象
    - 优点:好理解,代码清晰,不复杂

AOP的源码中用到了两种动态代理来实现拦截切入功能

  • jdk动态代理和cglib动态代理。
    - jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
    - 总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。

定义一个接口

public interface Singer {
    //唱歌
    public void singing();
    //跳舞
    public void dancing();
}

实现接口的类

public class ZhangJie implements Singer{
    @Override
    public void singing() {
        System.out.println("正在唱歌");
    }

    @Override
    public void dancing() {
        System.out.println("正在跳舞");
    }
}

静态代理

		//首先要实例化的对象是张杰(也就是我们的目标对象)
        Singer singer = new ZhangJie();//多态/向上转型

        //实例化代理对象来调用其方法(在调用目标对象前后做相关操作)
        ProxyWang proxyWang = new ProxyWang(singer);
        proxyWang.singing();

动态代理

		//目标对象
        Singer singer = new ZhangJie();
        //jdk提供了这样一个类,代表代理的意思
        //第一个参数是类加载器
        //第二个参数是目标对象的class
        //第三个参数是代理对象
        //返回值类型为Object
        Singer s = (Singer) Proxy.newProxyInstance(singer.getClass().getClassLoader(),new Class[]{Singer.class},new ProxyAi(singer));
        s.singing();

cglib 生成的动态代理

public class CglibProxy implements MethodInterceptor {

    //实例化我们的目标对象
    private Object obj;

    public Object getObj(Object obj) {
        this.obj = obj;
        //得到创建代理对象的对象
        Enhancer enhancer = new Enhancer();
        //设置类加载器
        enhancer.setClassLoader(this.obj.getClass().getClassLoader());
        //设置其父类
        enhancer.setSuperclass(this.obj.getClass());
        //设置回调,只要走下面这方法都会走回调
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();

    }

    //这个方法就是在你待用目标对象的方法都会执行这个方法
    //第一个参数是代理对象/第二个参数是目标对象的方法/第三个参数是目标对象的方法参数/第四个参数是代理对象的拦截方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ob = null;
      if (method.getName().equals("singing")){
           System.out.println("设备已经安装成功");
           ob = method.invoke(obj, objects);
           System.out.println("唱歌结束");
       }else if (method.getName().equals("dancing")) {
           System.out.println("舞台已经搭建成功,show time");
           ob = method.invoke(obj, objects);
           System.out.println("舞会结束");
       }
        return ob;
    }
}
 		//实例化一个cglibProxy 代理对象类
        CglibProxy cglibProxy = new CglibProxy();
        Singer singer = new ZhangJie();
        //这个参数代表我们的目标对象
        Singer singer1 = (Singer) cglibProxy.getObj(singer);
        singer1.dancing();

Spring AOP的配置元素

在这里插入图片描述

设置的步骤

spring 要配置的bean

<bean id="aopError" class="com.offcn.aop.AopError"></bean>
public class AopError {
    public void afterError(JoinPoint joinPoint,RuntimeException e){
        System.out.println(joinPoint.getSignature().getName()+"这个方法的异常信息"+e.getMessage());
    }

    public void after(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature().getName());
    }
    //环绕通知 传递的是 JoinPoint 的子类 ProceedingJoinPoint
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println(proceedingJoinPoint.getTarget()+ Arrays.toString(proceedingJoinPoint.getArgs()));
        //调用此方法执行目标对象相应的方法  这个方法相当于一个分割线,也就是前置通知和后置通知的分割线
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(proceedingJoinPoint.getSignature().getName());
    }
}

设置切面、切点、异常,前置,后置,环绕通知

	<!-- 设置切面 -->
    <aop:config>
        <!-- 配置切点 -->
        <aop:pointcut id="pointcut" expression="execution(* com.offcn.service.UserService.*(..))"></aop:pointcut>
        <!-- 引入增强bean -->
        <aop:aspect ref="aopError">
            <!-- 配置异常后置增强,这个方法必须引入增强bean的方法, -->
            <aop:after-throwing method="afterError" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
            <!-- 后置增强 -->
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
            <!-- 前置增强 -->
            <aop:before method="after" pointcut-ref="pointcut"></aop:before>
            <!-- 环绕增强 -->
            <aop:around method="around" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>	
    </aop:config>

测试 Test

public class Test {
    @org.junit.Test
    public void test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}

注解前置、后置、环绕、异常通知

@Aspect
public class AopError {
    @AfterThrowing(pointcut = "execution(* com.offcn.service.UserService.*(..))",throwing = "e")
    public void afterError(JoinPoint joinPoint, RuntimeException e){
        System.out.println(joinPoint.getSignature().getName()+"这个方法的异常信息"+e.getMessage());
    }

    public void after(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature().getName());
    }

    @Around("execution(* com.offcn.service.UserService.*(..))")
    //环绕增强 传递的是 JoinPoint 的子类 ProceedingJoinPoint
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println(proceedingJoinPoint.getTarget()+ Arrays.toString(proceedingJoinPoint.getArgs()));
        //调用此方法执行目标对象相应的方法  这个方法相当于一个分割线,也就是前置增强和后置增强的分割线
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
       System.out.println(proceedingJoinPoint.getSignature().getName());
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43332168/article/details/82988208