尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述 前言**???本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~??感兴趣的小伙伴看一看小编主页-CSDN博客?? 你的点赞就是小编不断更新的最大动力****??那么废话不多说直接开整吧~~目录???1.切点表达式??1.1execution表达式???1.2annotation1.自定义注解2.切面类3.添加注解???2.Spring AOP原理??2.1代理模式1.静态代理了解2.动态代理八股??2.2Spring AOP原理总结???3.总结###??1.切点表达式我们在上一期了解到了关于通知中切点表达式execution表达式但是没有做很详细的介绍不仅如此在切点表达式中有两种方式下面来一一介绍#### ??1.1execution表达式切点表达式的结构如下所示 execution(访问修饰符 返回类型 包名.类名.法(法参数) 异常)而在我们上期写的表达式中格式如下 Around(execution(com.example.springaop.controller..(…))“)解释* 匹配任意字符只匹配个元素(返回类型, 包, 类名, 法或者法参数… 匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数上述的controller后的两个 * 代表就是每一层括号里的 … 就是表示任意的方法参数这里也可以代表无参数举例 execution(public String com.example.demo.controller.TestController.t1())解释testcontroller类下的名为t1的方法并且这里的方法是无参的访问修饰符可以省去 execution(* com.example.demo.controller.TestController.t1())解释这里可以访问的就是没有任何限制的访问权限匹配所有返回类型 execution(* com.example.demo.controller.TestController.*())解释这里匹配所有的方法这些方法是无参的 execution(* com.example.demo.controller.*.*(…))解释这里匹配controller包下所有类的所有方法 execution(* com…TestController.*(…))解释这里匹配com包下的所有testcontroller类的所有方法#### ??1.2annotation我们在上述的简述中了解到关于execution表达式如何作用于匹配某个方法和类但是当存在两个类都要进行匹配并且里面的方法也是无规则的那如何呢此时就要annotation进行操作了假如有以下的两个控制类 RestController RequestMapping(”/test) public class TestController { RequestMapping(“/t1”) public String test1(){ log.info(“这是t1执行”); return “t1”; } RequestMapping(“/t2”) public String test2(){ log.info(“这是t2在执行”); return “t2”; } } RestController RequestMapping(“/user”) public class UserController { RequestMapping(“/u1”) public String user1(){ return “u1”; } RequestMapping(“/u2”) public String user2(){ return “u2”; } } 那么此时我们要将第一个控制器类的第一个方法进行匹配以及第二个控制器类的方法进行匹配那么此时的操作就是如下所示##### 1.自定义注解代码如下所示 //定义自己的注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface MyAspect { } Target 标识了 Annotation 所修饰的对象范围, 即该注解可以在什么地.Retention 指Annotation被保留的时间短, 标明注解的命周期##### 2.切面类使 annotation 切点表达式定义切点, 只对 MyAspect 效代码如下所示 Aspect Component Slf4j public class MyAnnotationAspect { Before(“annotation(com.example.springaop.config.MyAspect)”) public void doBefore(){ log.info(“before start…”); } After(“annotation(com.example.springaop.config.MyAspect)”) public void doAfter(){ log.info(“after start…”); } } 这里小编设置了两种通知类第一种是在目标方法执行前进行执行第二种通知是在方法执行后进行执行当然表达式里的的是我们自己注解的全限定路径加上我们的定义注解名称##### 3.添加注解代码如下所示 MyAspect RequestMapping(“/t1”) public String test1(){ log.info(“这是t1执行”); return “t1”; }这是第一个类的第一个方法 MyAspect RequestMapping(“/u2”) public String user2(){ return “u2”; }这是第二个类的第二个方法此时就与这两个方法进行了匹配那么就可执行切面通知了###??2.Spring AOP原理#### ??2.1代理模式代理模式也叫委托模式定义为其他对象提供种代理以控制对这个对象的访问. 它的作就是通过提供个代理类, 让我们在调标法的时候, 不再是直接对标法进调, 是通过代理类间接调其实就是中介的作用 1. Subject: 业务接类. 可以是抽象类或者接(不定有) 2. RealSubject: 业务实现类. 具体的业务执, 也就是被代理对象.真正的房东 3. Proxy: 代理类. RealSubject的代理中介这里的代理模式分为两种静态代理:由程序员创建代理类或特定具动成源代码再对其编译, 在程序运前代理类的 .class 件就已经存在了.动态代理:在程序运时, 运反射机制动态创建成##### 1.静态代理了解代码如下定义一个subject业务接口 public interface SubjectHouse { void rentHouse(); }让代理对象继承这个接口并写出要代理什么 public class RealSubjectHouse implements SubjectHouse{ //真正的房东 Override public void rentHouse(){ System.out.println(“我是房东我要出租房子”); } } 最后中介进行代理 public class HouseProxy implements SubjectHouse{ private SubjectHouse subjectHouse; public HouseProxy(SubjectHouse subjectHouse) { this.subjectHouse subjectHouse; } Override public void rentHouse() { System.out.println(“我是中介开始代理”); subjectHouse.rentHouse(); System.out.println(“我是中介结束代理”); } } 解释这里使用了一个向上转型的思想这里实际指定就是代理的对象就是realsubjecthouse即真正的房东从上述代码可以看出, 我们修改接(Subject)和业务实现类(RealSubject)时, 还需要修改代理类 (Proxy). 同样的, 如果有新增接(Subject)和业务实现类(RealSubject), 也需要对每个业务实现类新增代理类(Proxy).##### 2.动态代理八股相于静态代理来说动态代理更加灵活. 我们不需要针对每个标对象都单独创建个代理对象, 是把这个创建代理对象的作推迟到程序运 时由JVM来实现. 也就是说动态代理在程序运时, 根据需要动态创建成 这里的动态代理分为两种 JDK动态代理 CGLIB动态代理JDK动态代理代码了解public class JDKInvocationHandler implements InvocationHandler { private Object target;//目标对象 public JDKInvocationHandler(Object target) { this.target target; } /* 代理对象 通过 invoke调用 目标对象的方法 */ Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(“我是代理, 开始代理”); //通过反射, 调用目标对象的方法 Object result method.invoke(target, args); System.out.println(“我是代理, 结束代理”); return result; } } 进行main方法调用 SubjectHouse proxy (SubjectHouse) Proxy.newProxyInstance( SubjectHouse .class.getClassLoader(), new Class[]{SubjectHouse .class}, new JDKInvocationHandler(target)); proxy.rentHouse();newProxyInstance() , 这个法主要来成个代理对象Loader: 类加载器, 于加载代理对象. interfaces : 被代理类实现的些接(这个参数的定义, 也决定了JDK动态代理只能代理实现了接的些类) h : 实现了 InvocationHandler 接的对象target被代理对象CGLIB动态代理代码添加依赖 cglib cglib 3.3.0 public class CGLibMethodInterceptor implements MethodInterceptor { private Object target; public CGLibMethodInterceptor(Object target) { this.target target; } /* 调用代理对象的方法/ Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println(“我是中介, 开始代理”); Object result method.invoke(target, args); System.out.println(“我是中介, 结束代理”); return result; } }最后进行调用 SubjectHouse houseSubject (SubjectHouse) Enhancer.create( target.getClass(), new CGLibMethodInterceptor(target)); houseSubject.rentHouse();上述存在一个比较重要的知识点JDK动态代理存在一个致命的问题即只能代理接口不能代理类而CGLIB既可以代理接口又可以代理类#### ??2.2Spring AOP原理总结spring AOP原理是要从源码进行解读的但是源码过于复杂最终的情况就是如下所示在源码中的代理有个重要的属性: proxyTargetClass, 默认值为false. 也可以通过程序设置这里的默认值是根据不同情况来进行定义的大致意思就是在proxyTargetClass为false时在实现接口时使用JDK代理只实现类的情况下使用CGLIB代理若proxyTargetClass,为true那么所有实现方式都使用CGLIB代理 Spring默认使用的的proxyTargetClass为false但是在spring boot2.x之后proxyTargetClass默认为true使用CGLIB进行代理当然我们可以使用配置项进行配置 spring.aop.proxy-target-classfalse这里设置proxyTargetClass默认为false###??3.总结本期主要讲解了关于切点表达式的两种表达方式以及Spring AOP实现原理的两种代理模式即JDK代理以及CGLIB代理最后进行Spring AOP原理的总结???~~~~最后希望与诸君共勉共同进步*???以上就是本期内容了 感兴趣的话就关注小编吧。*???期待你的关注~~~*
返回列表