0
点赞
收藏
分享

微信扫一扫

Spring(三)aop

丹柯yx 2022-01-21 阅读 38

AOP  面向切面编程
底层动态代理  一种动态代理的规范

1、jdk动态代理:使用java反射包中的类和接口实现动态代理的功能

反射包java.lang。reflect,里面的三个类

2、cglib动态代理(了解):cglib是第三方工具库,创建代理对象

原理:继承,通过继承目标类,继承他的子类,在子类中重写父类中同名的方法,实现功能的修改。
因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final
cglib的要求比较宽松,只要能继承就行,所以在很多框架都有使用

动态代理的作用:

1、在目标类源代码不改变的情况下,增加功能
2、减少代码的重复
3、专注业务逻辑代码
4、解耦合,让你的业务功能和日志、事务非业务功能分离

面向切面编程: Aspect orient Programing
Aspect:切面,对你的目标类增加的功能,就是切面。
特点:非业务方法 ,可以独立使用
orient:面向,对着
Programing:编程

如何理解aop:
oop:面向对象编程
aop:面向切面编程

1、需要在分析项目功能时,找出切面。
2、合理的安排切面的执行时间
3、合理的安排切面执行的位置,在哪个类,哪个方法增强功能

术语:
Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能
        日志,事务,统计信息,参数检查,权限验证
JoinPoint:连接点,连接业务方法和切面的位置。就是某个类中的业务方法
pointcut:切入点,多个连接点方法的集合
目标对象:给哪个类的方法增加功能
Advice通知:通知表示切面功能执行的时间

一个切面的三个要素:
1、切面的功能代码,切面干什么
2、切面的执行位置,使用pointcut表示切面的执行位置
3、切面的执行时间,使用advice表示时间,在目标方法之前,还是之后

aop的实现:
AOP  面向切面编程
底层动态代理  一种动态代理的规范
aop的技术实现框架:
spring:spring在内部实现的aop规范,能做aop的工作
        spring主要在事务处理时使用aop
        我们在项目开发中很少使用spring的sop,因为spring的aop比较笨重
aspectJ:一个开源的专门做aop的框架,spring集成类aspectJ,通过spring就能使用aspectJ的功能
实现方式:
1、xml配置方式:配置全局事务
2、注解实现:五个注解

aspectJ框架使用:
1、切面的执行时间,这个执行时间在规范中叫做advice(通知,增强)
    在aspectJ这个框架中用注解表示,也可以使用xml配置文件中的标签
    @Before
    @AfterReturning
    @Around
    @AfterThrowing
    @@After
2、切面的执行位置,使用的是切入点表达式

使用aop:
目的是给以及存在的一些类和方法,增加额外的功能,前提是不改变原来的类的代码
使用aspectJ的步骤:
1、新建maven项目
2、添加依赖 spring依赖和aspect依赖junit单元测试
3、创建目标类:接口和他的实现类
要做的是给类中的方法增加功能
4、创建切面类:普通类


/**
 * @Aspect是aspectJ的注解
 *      作用:表示当前类是切面类
 *      切面类:使用类给业务方法增加功能的类,在这个类中有切面的功能代码
 *      位置:类定义的上面
 */
@Aspect
public class Myaspect {}


    1、在类的上面加入@Aspect

 /**
     * 定义方法,方法是实现切面功能的
     * 方法定义要求:
     *      公共方法
     *      没有返回值
     *      方法名称自定义
     *      方法可以有参数,也可以没有
     *      如果有参数,参数不是自定义的,有几个参数类型可以使用
     */


    2、在类中定义方法,方法就是切面要执行的功能代码
       在方法的上面加入aspectJ只能够的通知注解,例如@before
       还需要切入点表达式execution()


5、创建spring的配置文件,声明对象,把对象交给容器统一管理。

<!--    声明切面对象-->
    <bean id="myAspect" class="ba02.Myaspect"></bean>
<!--    声明自动代理生成器L:使用aspectJ框架内部的功能,创建目标对象的代理对象
        创建代理对象是在内存中做的,修改目标对象的内存中的结构。创建为代理对象
        所以目标对象就是被修改后的代理对象

        aspectj-autoproxy:会把spring容器中的所有目标对象,一次性生成代理对象
    -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


    声明对象你可以使用注解或xml配置文件
    1、声明目标对象
    2、声明切面对象
    3、声明aspectJ框架中的自动代理生成器标签
       自动代理生成器:用来完成代理对象的自动创建功能


6、测试类,从spring容器中获取目标对象(实际是代理对象)
    通过代理执行方法,实现aop的功能增强

表示切面位置的切入点表达式:execution(访问的修饰符  返回值  包名.类名.方法名称(方法的参数)  异常)

execution(public void ba02.SomeServiceImpl.doSome(String,Integer)
execution表达式
(方法的类型  方法的返回值类型  方法名的全限定名称 (参数的数据类型))

五个注解

@Before

 /**
     *     @Before() 前置通知注解
     *     属性value,是切入点表达式,表示切面的功能执行的位置
     *     位置:在方法的上面
     *     特点:
     *          1、在目标方法之前执行的
     *          2、不会改变目标方法的执行结果
     *          3、不会影响目标方法的执行
     */
  @Before("execution(public void ba02.SomeServiceImpl.doSome(String,Integer))")
  /**
     * 指定通知方法中的参数: joinpoint
     * joinpoint:业务方法,要加入切面功能的业务方法
     *          作用是:可以在通知方法中获取方法执行时的信息,例如方法名称吗,方法的实参
     *          如果你的切面功能中需要用到方法的信息,就加入joinpoint
     *          这个joinpoint参数的值是由框架赋予,必须是第一个参数的位置
     */
    @Before("execution( void *..SomeServiceImpl.doSome(..)) ")
    public void mybefore(JoinPoint jp){
        System.out.println("获取方法的完整定义: "+jp.getSignature());
        System.out.println("方法的名称: "+jp.getSignature().getName());
        System.out.println(jp.getSignature().getDeclaringType());
        System.out.println(jp.getSignature().getDeclaringTypeName());
}


@AfterReturning

/**
     * @AfterReturning:后置通知
     * 属性:1、value:切入点表达式
     *      2、returning 自定义的变量,表示目标方法的返回值的
     *                  自定义变量名必须和通知方法的形参名一样
     * 位置:方法定义上面
     * 特点:
     *      1、在目标方法之后执行
     *      2、能够获取目标方法的返回值,可以根据这个返回值做不同的功能处理
     *      Object res=doother();
     *      3、可以修改这个返回值
     *
     *      后置执行的通知:
     *      object res=doother()
     *      myafterReturning(Object res);
     *
     * @param res
     */

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doother(..))",
            returning = "res")
    public void myafterReturning(Object res){
//       Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("后置通知:在目标方法之后执行,获取的返回值是:"+res);
        if(res.equals("abcd")){
            //做一些功能
        }else{
            //其他功能
        }
        //修改目标的返回值,看一下是否会影响
        if(res!=null){
            res="hello aspectJ";
            System.out.println("res=="+res);

        }
    }


@Around

 /**
     * @Around 环绕通知
     *  属性value:切入点表达式
     *  位置:在方法定义的上面
     *  特点:
     *      1、功能最强的通知
     *      2、在目标方法的前后都能增强
     *      3、控制目标方法是否被调用执行
     *      4、可以修改原来的目标方法的执行结果,影响最后的调用结果
     *
     * 环绕通知相当于jdk的动态代理
     *
     * 参数ProceedingJoinPoint pjp 相当于method
     * 作用:用来执行目标方法
     * 返回值:目标方法的执行结果,可以被修改
     *ProceedingJoinPoint 继承了JoinPoint
     * 可以获取参数值
     *
     * 环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务
     * @param pjp
     */
    @Around("execution(* *..SomeServiceImpl.dofrist(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取参数
        String name="";
        Object[] args=pjp.getArgs();
        if(args!=null&&args.length>1){
            Object arg=args[0];
            name=(String) arg;
        }

        //实现环绕通知
        Object res=null;//方法调用返回结果
        System.out.println("环绕通知前:"+new Date());
        if(name.equals("lixianji")){
            //符合条件 调用方法
            res=pjp.proceed();//method.invoke
        }
        
        //2、在目标方法的前后加入功能
        System.out.println("环绕通知后!");

//        修改目标方法的执行结果,影响方法最后的调用结果
        if(res!=null){
            res="hello AspectJ";
        }

        return res;
    }

@Pointcut

 /**
     * @pointcut :定义和管理接入点
     *            如果你的项目中有多个切入点表达式 是重复的 是可以复用的
     *            使用@pointcut
     *            属性:value 切入点表达式
     *            位置:自定义方法上面
     *  特点:
     *  当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
     *  其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式
     *
     */
    @Pointcut(value = "execution(* *..SomeServiceImpl.dosecond(..))")
    private   void mypt(){

    }


@AfterThrowing

/**
     *    @AfterThrowing 异常通知
     *    属性:1、value 切入点表达式
     *          2、throwing 自定义的变量  表示目标方法抛出的异常对象
     *          变量名必须和方法的参数名一样
     *     特点:
     *     1、在目标方法抛出异常时执行的
     *     2、可以做异常的监控程序,监控目标方法执行时是不是有异常
     *          如果有可以发送邮件,短信来通知
     * @param ex
     */
    @AfterThrowing(value = "mypt()"
    ,throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知,执行"+ex.getMessage());

    }


@After

/**
 * 最终通知方法的定义格式:
 * 1、public
 * 2、没有返回值
 * 3、方法名称自定义
 * 4、方法有没有参数,如果还有是JoinPoint
 */
    /**
     *    @After 异常通知
     *    属性:1、value 切入点表达式
     *    位置L:方法上面
     *     特点:
     *     1、总是会执行
     *     2、在目标方法之后执行
     */
    @After(value = "execution(* *..SomeServiceImpl.dosecond(..))")
    public void myafter(){
        System.out.println("执行最终通知 ,终是会通知");
        //一般做资源清除

    }
举报

相关推荐

0 条评论