spring框架 - AOP

spring - AOP


  •     一、代理模式

        基本原则

            1. 代理类与委托类具有相似的行为(共同)
            2. 代理类增强委托类的行为

        静态代理

            代理角色固定
            代理类和委托类实现相同的接口

                父接口
                代理类
                委托类
                单元测试

详解参考:静态代理之结婚与婚庆公司案例


 

动态代理

通过反射机制在程序运行期动态的为目标对象创建代理对象

  JDK实现
                实现InvocationHandler接口,重写invoke()
                获取目标类
                程序运行期间动态的创建代理角色
                    获取代理对象
                        1.类加载器
                        2.目标类实现的接口class
                        3.当前类

package com.lwx.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/30 21:50 .
 */
public class JdkHandler implements InvocationHandler {

    // 目标类
    private Object you;

    public JdkHandler(Object you) {
        this.you = you;
    }

    /**
     * 程序运行期间动态创建代理角色
     * @return
     */
    public Object getProxy() {

        /**
         * 获取代理对象
         * 1.类加载器
         * 2.目标类实现的接口class
         * 3.当前类
         */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),you.getClass().getInterfaces(),this);
    }

    public void before() {
        System.out.println("正在紧张布置婚礼现场。。。");
        System.out.println("放炮。。。开始~");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        // 执行真正的target方法
        Object invoke = method.invoke(you, args);
        after();
        return invoke;
    }

    public void after() {
        System.out.println("婚礼结束。。。开启人生第二阶段");
    }
}
package com.lwx.jdk;

import com.lwx.You;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/30 21:59 .
 */
public class JdkHandlerTest {

    @Test
    public void invoke() {
        // 委托类
        You you = new You();
        // 创建代理类
        JdkHandler jdkHandler = new JdkHandler(you);
        // 代理对象
        Object proxy = jdkHandler.getProxy();
        // 执行方法
        try {
            jdkHandler.invoke(proxy, You.class.getMethod("toMarry"), null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}


            cglib实现

          code generator library ,操作字节码。与jdk 提供的代理区别,Proxy:委托类必须有接口,制作过程比较快,执行慢;cglib:委托类可以没有接口,继承的思维来实现相似性,制作代理过程比较慢,执行快。主要解决没有接口类的代理实现。


                实现MethodInterceptor接口,重写intercept()
                获取目标类
                程序运行期间动态的创建代理角色
                    获取代理对象
                        1.创建Enhancer对象
                        2.设置父类
                        3.设置回调

package com.lwx.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/30 23:39 .
 */
public class CglibHandler implements MethodInterceptor {

    private Object you;

    public CglibHandler(Object you) {
        this.you = you;
    }

    /**
     * 运行期间创建代理对象
     */
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(you.getClass());
        // 设置回调
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public void before() {
        System.out.println("正在紧张布置婚礼现场。。。");
        System.out.println("放炮。。。开始~");
    }

    public void after() {
        System.out.println("婚礼结束。。。开启人生第二阶段");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       before();

       Object obj = method.invoke(you, objects);

       after();
        return obj;
    }
}
package com.lwx.cglib;

import com.lwx.You;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/30 23:50 .
 */
public class CglibHandlerTest {

    @Test
    public void intercept() {
        // 委托类
        You you = new You();
        // 代理类
        CglibHandler cglibHandler = new CglibHandler(you);
        // 代理对象
        You proxy = (You) cglibHandler.getProxy();
        proxy.toMarry();
    }
}


            两者区别
                JDK:Proxy:委托类必须有接口,制作过程比较快,执行慢
                    原理是实现接口
                cglib:委托类可以没有接口,继承的思维来实现相似性,制作代理过程比较慢,执行快
                    原理是继承


  •     二、日志处理带来的问题


  •     三、理解AOP

        what
             Aspect Oriented Programing 面向切面编程,相比较oop 面向对象编程来说,Aop 关注的不再是程序代码中某个类,某些方法,而aop 考虑的更多的是一种面到面的切入即层与层之间的一种切入,所以称之为切面。


        can
            用于日志记录,性能统计,安全控制,事务处理等方面,实现公共功能性的重复使用


        advantage
            1. 降低模块与模块之间的耦合度,提高业务代码的聚合度。(高内聚低耦合
            2. 提高了代码的复用性。
            3. 提高系统的扩展性。


  •     四、AOP基本概念

        1.JoinPoint(连接点)
            被拦截到的每个点,spring 中指被拦截到的每一个方法

        2.PointCut(切入点)
            对连接点进行拦截的定义

        3.Advice(通知)
            拦截到每一个连接点即(每一个方法)后所要做的操作
            i. 前置通知(前置增强) --before() 执行方法前通知
            ii.返回通知(返回增强)--afterReturn 方法正常结束返回后的通知
            iii.异常抛出通知(异常抛出增强)--afetrThrow()
            iv.最终通知—after 无论方法是否发生异常,均会执行该通知。
            v.环绕通知—around 包围一个连接点(join point)的通知,如方法调用。

         在环绕通知里面,必须执行目标方法

 

注意:

        虽然环绕通知包含了另外三种,但还是要依据业务逻辑来选择,这样有利于代码的编程量
        并且环绕通知最好不要和另外三种混用,并且并许执行目标方法proceed().

        4.Aspect(切面)
            切入点与通知的结合

        5.Target(目标对象)

        6.Weave(织入)
            将切面应用到目标对象并生成代理对象的这个过程即为织入。

        7.Introduction(引入)
            在不修改原有应用程序代码的情况下,在程序运行期为类动态添加方法或者字段的过程称为引入


  •     五、使用AOP解决日志处理问题

        注解方式

            1.Jar包坐标引入
            2.xml文件配置
                添加命名空间aop
                配置aop代理 <aop:aspectj-autoproxy/>
            3.业务方法
                aop实现类
                切入点

aop实现类

package com.lwx.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/31 21:55 .
 *
 * 声明切面组件
 */
@Component
@Aspect
public class LogCut {
    /**
     * 连接点
     * 切入点
     * 通知
     */
    @Pointcut("execution(* com.lwx.service..*.*(..))")
    public void cut() {

    }

    @Before(value = "cut()")
    public void before() {
        System.out.println("before...");
    }

    @AfterReturning(value = "cut()")
    public void afterRrturning() {
        System.out.println("afterReturning ...");
    }

    @After(value = "cut()")
    public void after() {
        System.out.println("after ...");
    }

    @AfterThrowing(value = "cut()",throwing = "e")
    public void afterThrowing(Exception e) {
        System.out.println("afterThrowing ..." + e);
    }

    /**
     * 声明环绕通知并将通知应用到切入点上
     * 方法执行前后通过环绕通知定义相应处理
     */
    @Around(value = "cut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("around before ...");
        System.out.println("around afterReturning ...");
        System.out.println(pjp.getTarget() + "---" + pjp.getSignature());
        Object result = pjp.proceed();
        System.out.println("around after ...");
        return result;
    }
}

切入点

package com.lwx.service;

import org.springframework.stereotype.Service;

/**
 * By:刘文旭 ~ https://blog.csdn.net/qq_40414738
 * Date:2018/10/31 22:20 .
 */
@Service
public class UserService {
    public void test() {
        // int i = 1/0;
        System.out.println("测试 LogCut...");
    }
}

           getSignature():方法签名
           getArgs():方法的实参
           getTarget(): 目标对象


        xml配置方式

        Aop 匹配方法规则表达式语言

                  切入点表达式

执行任意公共方法execution(public *(..))
执行任意的set 方法execution(* set*(..))
执行com.lwx.service 包下任意类的任意方法execution(* com.lwx.service.*.*(..))
执行所有类的所有方法execution(* *.*(..))
执行com.lwx.service 包以及子包下任意类的任意方法execution(* com.lwx.service..*.*(..))

  •     六、AOP面试常见问题


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值