spring aop

spring基础之-1-spring aop

一、相关概念

面向切面编程 aop (aspect oriented program)

将程序中不同业务的各个小而类似的功能抽取出来,在运行时动态织入增强的逻辑;

一般用于日志等的横向开发。

二、实现方式

1、jdk动态代理: 基于 目标类的接口来生成动态代理对象

  • 接口

    package org.jnr.proxy.jdk;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:02
     **/
    public interface TargetInterface {
        void insert();
    
        void delete();
    
        void update();
    
        void queryOne();
    
        void queryListAll();
    }
    
  • 目标对象

    package org.jnr.proxy.jdk;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:03
     **/
    public class Target implements TargetInterface {
        @Override
        public void insert() {
            System.out.println("insert running ...");
        }
    
        @Override
        public void delete() {
            System.out.println("delete running ...");
        }
    
        @Override
        public void update() {
            System.out.println("update running ...");
        }
    
        @Override
        public void queryOne() {
            System.out.println("queryOne running ...");
        }
    
        @Override
        public void queryListAll() {
            System.out.println("queryListAll running ...");
        }
    }
    
  • 切面通知类

    package org.jnr.proxy.jdk;
    
    /**
     * @program: Spring复习
     * @description: 通知类
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:05
     **/
    public class Advice {
        public void before() {
            System.out.println("前置通知");
        }
    
        public void afterRetuning() {
            System.out.println("后置通知");
        }
    }
    
  • 实现/测试

    package org.jnr.proxy.jdk;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:04
     **/
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 基于 jdk 接口 目标对象 代理对象 的动态代理
     */
    public class ProxyTest {
        public static void main(String[] args) {
            //目标对象
            final Target target = new Target();
            //通知对象
            final Advice advice = new Advice();
            //代理对象
            Object o = Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),   //目标对象类加载器
                    target.getClass().getInterfaces(),//目标对象所有接口 数组
                    new InvocationHandler() {//代理对象执行的方法,其中执行目标对象方法和增强逻辑
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            advice.before();//前置通知
                            Object invoke = method.invoke(target, args);//目标方法
                            advice.afterRetuning();//后置通知
                            return invoke;
                        }
                    }
            );
            //由于 代理对象 和 目标对象 都是实现了 接口,因此是兄弟类,只能强转为接口(父)
            TargetInterface targetInterface = (TargetInterface) o;
            //执行 目标方法,自动增强逻辑
            targetInterface.insert();
            targetInterface.delete();
            targetInterface.update();
            targetInterface.queryOne();
            targetInterface.queryListAll();
        }
    }
    
  • 结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfkIoM8T-1605925719261)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20201121101645403.png)]

2、 cglib动态代理 :通过生成一个继承自目标类的代理类来实现

  • 目标对象

    package org.jnr.proxy.cglib;
    
    import org.jnr.proxy.jdk.TargetInterface;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:03
     **/
    public class Target {
        public void insert() {
            System.out.println("insert running ...");
        }
    
        public void delete() {
            System.out.println("delete running ...");
        }
    
        public void update() {
            System.out.println("update running ...");
        }
    
        public void queryOne() {
            System.out.println("queryOne running ...");
        }
    
        public void queryListAll() {
            System.out.println("queryListAll running ...");
        }
    }
    
  • 切面类

    package org.jnr.proxy.cglib;
    
    /**
     * @program: Spring复习
     * @description: 通知类
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:05
     **/
    public class Advice {
        public void before() {
            System.out.println("前置通知");
        }
    
        public void afterRetuning() {
            System.out.println("后置通知");
        }
    }
    
  • 实现/测试

    package org.jnr.proxy.cglib;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-20 10:04
     **/
    
    import org.jnr.proxy.jdk.TargetInterface;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 基于 cglib 继承自 目标对象的 代理对象 的动态代理
     */
    public class ProxyTest {
        public static void main(String[] args) {
            //目标对象
            final Target target = new Target();
            //通知对象
            final Advice advice = new Advice();
    
    
            //创建增强器
            Enhancer enhancer = new Enhancer();
    
            //设置父类
            enhancer.setSuperclass(target.getClass());
    
            //设置回调方法
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    advice.before();//前置通知
                    Object invoke = method.invoke(target, args);//目标方法
                    advice.afterRetuning();//后置通知
                    return invoke;
                }
            });
            //生成代理对象
            Target proxy = (Target) enhancer.create();
            //执行 目标方法,自动增强逻辑
            proxy.insert();
            proxy.delete();
            proxy.update();
            proxy.queryOne();
            proxy.queryListAll();
        }
    }
    
  • 结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ypt8VaV3-1605925719275)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20201121101611197.png)]

三、使用 spring aop

spring aop 需要导入 spring-aop 和 spring-aspect (或 spring-aspectjweave,是第三方的,但是spring也集成了,这个包在 spring-aspects依赖中有,因为 aspectj 做的比 spring 提供的好,所以一般都是用他的)

1、 基于 xml 配置方式

  • 编写目标对象接口

    package org.jnr.aop.xml;
    
    /**
     * @program: Spring复习
     * @description: 目标对象接口
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:07
     **/
    public interface TargetInterface {
        void insert();
    
        void delete();
    
        void update();
    
        void select();
    }
    
  • 编写目标对象

    package org.jnr.aop.xml.impl;
    
    import org.jnr.aop.xml.TargetInterface;
    
    /**
     * @program: Spring复习
     * @description: 目标对象
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:07
     **/
    public class Target implements TargetInterface {
        @Override
        public void insert() {
            System.out.println("Target insert ...");
        }
    
        @Override
        public void delete() {
            System.out.println("Target delete...");
        }
    
        @Override
        public void update() {
            System.out.println("Target update...");
        }
    
        @Override
        public void select() {
            System.out.println("Target select ...");
        }
    }
    
  • 编写切面类(用于增强/通知的类)

    package org.jnr.aop.xml;
    
    /**
     * @program: Spring复习
     * @description: 通过xml配置aop
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:05
     **/
    public class MyAspectByXml {
        public void before() {
            System.out.println("前置通知");
        }
    
        public void afterReturning() {
            System.out.println("后置通知");
        }
    }
    
  • 在springioc容器中加载上面目标对象、切面类

    <bean id="target" class="org.jnr.aop.xml.impl.Target"></bean>
    <bean id="myAspectByXml" class="org.jnr.aop.xml.MyAspectByXml"></bean>
    
  • 在springioC容器中配置织入过程(需要导入aop命名空间和文件)(即配置切面(切入点+通知方法))

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    ">
        <!--  加载注入 上面目标对象、切面类 -->
        <bean id="target" class="org.jnr.aop.xml.impl.Target"></bean>
        <bean id="myAspectByXml" class="org.jnr.aop.xml.MyAspectByXml"></bean>
    
        <aop:config>
            <!--    配置切面    -->
            <aop:aspect ref="myAspectByXml">
                <!-- 配置切面 (切入点+增强方法) -->
                <aop:before method="before" pointcut="execution(public void org.jnr.aop.xml.impl.Target.*())"></aop:before>
            </aop:aspect>
        </aop:config>
    </beans>
    
  • 使用junit+spring-text 测试

    package org.jnr;
    
    import org.jnr.aop.xml.TargetInterface;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @program: Spring复习
     * @description: 通过 xml 方式配置使用 spring aop
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:13
     **/
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:Application-aop.xml")
    public class AopByXmlTest {
        @Autowired
        private TargetInterface target;
    
        @Test
        public void test01() {
            target.insert();
            target.delete();
            target.update();
            target.select();
        }
    }
    

2、基于注解的配置方式

  • 编写目标对象接口

    package org.jnr.aop.annotation;
    
    /**
     * @program: Spring复习
     * @description: 目标对象接口
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:07
     **/
    public interface TargetInterface {
        void insert();
    
        void delete();
    
        void update();
    
        void select();
    }
    
  • 编写目标对象

    package org.jnr.aop.annotation.impl;
    
    import org.jnr.aop.annotation.TargetInterface;
    import org.springframework.stereotype.Component;
    
    /**
     * @program: Spring复习
     * @description: 目标对象
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:07
     **/
    @Component
    public class Target implements TargetInterface {
        @Override
        public void insert() {
            System.out.println("Target insert ...");
        }
    
        @Override
        public void delete() {
            System.out.println("Target delete...");
        }
    
        @Override
        public void update() {
            System.out.println("Target update...");
        }
    
        @Override
        public void select() {
            System.out.println("Target select ...");
            System.out.println("即将抛出异常");
            int i = 1 / 0;
        }
    }
    
  • 编写切面类(用于增强/通知的类)

    package org.jnr.aop.annotation;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * @program: Spring复习
     * @description: 通过xml配置aop
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:05
     **/
    @Aspect
    @Component
    public class MyAspectByAnnotation {
        @Before(value = "execution(public void org.jnr.aop.annotation.impl.Target.*())")
        public void before(JoinPoint joinPoint) {
            System.out.println(joinPoint);
            System.out.println("前置通知");
        }
    
        @Around(value = "execution(public void org.jnr.aop.annotation.impl.Target.*())")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            Object proceed = pjp.proceed();//执行 目标方法,下面是增强逻辑
            //Proceeding JoinPoint :正在进行中的 连接点,也就是 切点 ;ProceedingJoinPoint 只能用于 环绕通知
            System.out.println("环绕通知");
            return proceed;
        }
    
        //将切点表达式 execution 抽取出来
        @Pointcut(value = "execution(public void org.jnr.aop.annotation.impl.Target.*())")//value="","" 都行
        public void pointCut() {
        }
    
        //可以直接用方法名使用切点表达式方法
        @After(value = "pointCut()")//value="","" 都行
        public void after() {
            System.out.println("最终通知");
        }
    
        //也可以用类.切点表达式方法()
        @AfterReturning(pointcut = "MyAspectByAnnotation.pointCut()")//pointcut="" , value="","" 都行
        private void afterReturning() {
            System.out.println("后置通知");
        }
    
        @AfterThrowing(value = "execution(* org.jnr.aop.annotation.impl*.*(..))")
        public void afterThrowing() {
            System.out.println("异常通知");
        }
    }
    
  • 使用注解在springioc容器中加载上面目标对象、切面类(@Component注解 @Aspect注解、各种通知注解@Before @After @AfterRetuning @Around @AfterThrowing )

    参见上面代码

  • 编写配置类 扫描包注入容器和 开启aop自动代理注解(@Configuration @ComponentScan @EnableAspectjAutoProxy或者在xml配置文件中 使用aop:aspectj-autoproxy/)

    package org.jnr.aop.annotation;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    /**
     * @program: Spring复习
     * @description:
     * @author: jin rong@JNR
     * @create: 2020-11-21 08:54
     **/
    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan(basePackages = "org.jnr.aop.annotation")
    public class AopConfiguration {
    }
    
  • 使用junit+spring-text 测试

    package org.jnr;
    
    import org.jnr.aop.annotation.AopConfiguration;
    import org.jnr.aop.annotation.TargetInterface;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /**
     * @program: Spring复习
     * @description: 通过 注解 方式配置使用 spring aop
     * @author: jin rong@JNR
     * @create: 2020-11-20 23:13
     **/
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AopConfiguration.class)
    public class AopByAnnotationTest {
        @Autowired
        private TargetInterface target;
    
        @Test
        public void test01() {
            target.insert();
            target.delete();
            target.update();
            target.select();
        }
    }
    

3、总结

  • ProceedingJoinPoint 参数只能用于环绕通知
  • aspectj 版本如果太低,比如 1.5.* ,在使用 JoinPoint 作为方法参数时会报 错,建议更新版本为 1.8.1,或者干脆不要单独导入 aspectj依赖,直接导入 spring-aspects 包,里面就有较新的 aspectj 包
  • 如果使用注解开发 aop ,记得要在 xml 中开启自动代理 注解 aop:apsect-autoproxy/ 或者在配置类中加上 @EnableAspectjAutoProxy 注解
  • spring 框架根据 目标类是否是实现类(有接口)来决定采用哪种 动态代理对象。

4、execution 表达式 大全(记得空格问题)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uv149H7p-1605925719284)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20201121100437179.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值