做安居客网站需要什么浏览器,什么是网站的功能模块,南充微网站建设,服务商类型是什么意思目录
1.切点表达式
1.1execution表达式
1.2 annotation
1.2.1自定义注解MyAspect
1.2.3添加自定义注解
2.Sping AOP原理
2.1代理模式
2.1.1静态代理
2.1.2动态代理
2.1.3JDK动态代理
2.1.4CGLIB动态代理
3.总结 承接上文#xff1a;详解Spring AOP#xff08;一annotation
1.2.1自定义注解MyAspect
1.2.3添加自定义注解
2.Sping AOP原理
2.1代理模式
2.1.1静态代理
2.1.2动态代理
2.1.3JDK动态代理
2.1.4CGLIB动态代理
3.总结 承接上文详解Spring AOP一
1.切点表达式
之前的代码中我们一直在使用切点表达式来描述切点.下面我们来介绍一下切点表达式的语法.切点表达式常见有两种表达方式 execution(.....):根据方法的签名来匹配 annotation(...):根据注解匹配
1.1execution表达式
execution() 是最常用的切点表达式, 用来匹配⽅法, 语法为:
execution(访问修饰符 返回类型 包名 .类名 .方法(方法参数) 异常)
其中访问修饰符和异常可以省略 切点表达式支持通配符表达*:匹配任意字符只匹配一个元素(返回类型包类名方法或者方法参数) 包名使用*表示任意包(一层包使用一个*) 类名使用*表示任意类 返回值使用*表示任意返回值类型 方法名使用*表示任意方法 参数使用*表示一个任意类型的参数 .. :匹配多个连续的任意符号可以通配任意层级的包或任意类型任意个数的参数 使用..配置包名标识此包以及此包下的所有子包 可以使用..配置参数任意个任意类型的参数 切点表达式示例TestController 下的 public修饰, 返回类型为String 方法名为t1, 无参方法
execution(public String com.example.demo.controller.TestController.t1())
省略访问修饰符
execution(String com.example.demo.controller.TestController.t1())
匹配所有返回类型
execution(* com.example.demo.controller.TestController.t1())
匹配TestController 下的所有无参方法
execution(* com.example.demo.controller.TestController.*())
匹配controller包下所有的类的所有方法
execution(* com.example.demo.controller.*.*(..))
匹配所有包下面的TestController
execution(* com..TestController.*(..))
匹配com.example.demo包下, 子孙包下的所有类的所有方法
execution(* com.example.demo..*(..))
1.2 annotation
execution表达式更适用有规则的如果我们要匹配多个无规则的方法比如TestController中的t1()和UserController中的u1()这两个方法. 这个时候我们使用execution这种切点表达式来描述就不是很方便了. 我们可以借助自定义注解的方式以及切点表达式annotation来描述这一类的切点实现步骤 1.编写自定义注解 2.使用annotation表达式来描述切点 3.在连接点的方法上添加自定义注解 准备测试代码 RequestMapping(/test) RestControllerpublic class TestController { RequestMapping(/t1)public String t1() {return t1;} RequestMapping(/t2)public boolean t2() {return true;}
}
RequestMapping(/user)RestControllerpublic class UserController {RequestMapping(/u1)public String u1(){return u1;}RequestMapping(/u2)public String u2(){return u2;}}
1.2.1自定义注解MyAspect
创建⼀个注解类(和创建Class文件⼀样的流程, 选择Annotation就可以了) import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)public interface MyAspect {} Target标识了Annotation所修饰的对象范围即该注解可以用在什么地方. 常用取值 ElementType.TYPE:用于描述类、接口(包括注解类型)或enum声明 ElementType.METHOD:描述方法 ElementType.PARAMETER:描述参数ElementType.TYPE_USE:可以标注任意类型 Retention指Annotation被保留的时间长短标明注解生命周期Retention的取值有三种 1.RetentionPolicy.SOURCE:表示注解仅存在于源代码中编译成字节码后会被丢弃.这意味着在运行时无法获取到该注解的信息只能在编译时使用.比如SuppressWarnings,以及lombok提供的注解Data,slf4j 2.RetentionPolicy.CLASS:编译时注解.表示注解存在于源代码和字节码中但在运行时会被丢弃.这意味着在编译时和字节码中可以通过反射获取到该注解的信息但在实际运行时无法获取.通常用于一些框架和工具的注解. 3.RetentionPolicy.RUNTIME:运行时注解.表示注解存在于源代码字节码和运行时中.这意味着在编译时字节码中和实际运行时都可以通过反射获取到该注解的信息.通常用于一些需要在运行时处理的注解如Spring的Controller ResponseBody 1.2.2切面类
使用annotation切点表达式定义切点只对MyAspect生效
切面类代码如下: Slf4jComponentAspectpublic class MyAspectDemo { //前置通知Before(annotation(com.example.demo.aspect.MyAspect))public void before(){log.info(MyAspect - before ...);}//后置通知After(annotation(com.example.demo.aspect.MyAspect))public void after(){log.info(MyAspect - after ...);}}
1.2.3添加自定义注解
在TestController中的t1()和UserController中的u1()这两个方法上添加自定义注解MyAspect,其他方法不添加 MyAspectRequestMapping(/t1)public String t1() {return t1;}MyAspectRequestMapping(/u1)public String u1(){return u1;}
运行程序测试接口
http://127.0.0.1:8080/test/t1 观察日志 可以看到切面通知被执行了
继续测试:
http://127.0.0.1:8080/test/t2, 切面通知未执行
http://127.0.0.1:8080/user/u1 , 切面通知执行
2.Sping AOP原理
上面我们主要学习了Spring AOP的应用接下来我们来学习SpringAOP的原理也就是Spring是如何实现AOP的. Spring AOP是基于动态代理来实现AOP的学习内容主要分以下两部分 1.代理模式 2.Spring AOP源码剖析
2.1代理模式
代理模式也叫委托模式. 定义为其他对象提供一种代理以控制对这个对象的访问.它的作用就是通过提供一个代理类让我们在调用目标方法的时候不再是直接对目标方法进行调用而是通过代理类间接调用. 在某些情况下一个对象不适合或者不能直接引用另一个对象而代理对象可以在客户端和目标对象之间起到中介的作用.使用代理前 使用代理后: 生活中的代理 艺人经纪人广告商找艺人拍广告需要经过经纪人由经纪人来和艺人进行沟通. 房屋中介房屋进行租赁时卖方会把房屋授权给中介由中介来代理看房房屋咨询等服务. 秘书/助理合作伙伴找老板谈合作需要先经过秘书/助理预约. 代理模式的主要角色 1. Subject: 业务接口类. 可以是抽象类或者接口(不⼀定有) 2. RealSubject: 业务实现类. 具体的业务执行 , 也就是被代理对象. 3. Proxy: 代理类. RealSubject的代理. 比如房屋租赁 Subject 就是提前定义了房东做的事情, 交给中介代理, 也是中介要做的事情 RealSubject: 房东 Proxy: 中介 UML类图如下: 代理模式可以在不修改被代理对象的基础上通过扩展代理类进行一些功能的附加与增强. 根据代理的创建时期代理模式分为静态代理和动态代理. 静态代理由程序员创建代理类或特定工具自动生成源代码再对其编译在程序运行前代理类的.class文件就已经存在了. 动态代理在程序运行时运用反射机制动态创建而成. 2.1.1静态代理 静态代理在程序运行前代理类的.class文件就已经存在了.(在出租房子之前中介已经做好了相关的工作就等租户来租房子了) 我们通过代码来加深理解.以房租租赁为例 1.定义接口(定义房东要做的事情也是中介需要做的事情) public interface HouseSubject {void rentHouse();
} 2.实现接口房东出租房子 public class RealHouseSubject implements HouseSubject{Overridepublic void rentHouse() {System.out.println(我是房东 , 我出租房⼦);}} 3.实现代理中介帮房东出租房子 public class HouseProxy implements HouseSubject{ //将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject houseSubject;} Overridepublic void rentHouse() { //开始代理System.out.println(我是中介 , 开始代理); //代理房东出租房⼦houseSubject.rentHouse(); //代理结束System.out.println(我是中介 , 代理结束); }} 4.编写测试用例 public class StaticMain {public static void main(String[] args) {HouseSubject subject new RealHouseSubject(); //创建代理类HouseProxy proxy new HouseProxy(subject); //通过代理类访问⽬标⽅法proxy.rentHouse(); }} 运行结果 上面这个代理实现方式就是静态代理 从上述程序可以看出虽然静态代理也完成了对目标对象的代理但是由于代码都写死了对目标对象的每个方法的增强都是手动完成的非常不灵活.所以日常开发几乎看不到静态代理的场景. 接下来新增需求中介又新增了其他业务代理房屋出售我们需要对上述代码进行修改 1.接口定义修改 public interface HouseSubject {void rentHouse();void saleHouse();} 2.接口实现修改 public class RealHouseSubject implements HouseSubject{ Overridepublic void rentHouse() {System.out.println(我是房东 , 我出租房⼦);} Overridepublic void saleHouse() {System.out.println(我是房东 , 我出售房⼦);}} 3.代理类修改 public class HouseProxy implements HouseSubject{ //将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject houseSubject;} Overridepublic void rentHouse() { //开始代理System.out.println(我是中介 , 开始代理); //代理房东出租房⼦houseSubject.rentHouse(); //代理结束System.out.println(我是中介 , 代理结束); }Overridepublic void saleHouse() { //开始代理System.out.println(我是中介 , 开始代理); //代理房东出租房⼦houseSubject.saleHouse(); //代理结束System.out.println(我是中介 , 代理结束); }} 从上述代码可以看出我们修改接口(Subject)和业务实现类(RealSubject)时还需要修改代理类(Proxy). 同样的如果有新增接口(Subject)和业务实现类(RealSubject),也需要对每一个业务实现类新增代理类(Proxy). 既然代理的流程是一样的有没有一种办法让他们通过一个代理类来实现呢?这就需要用到动态代理技术了 2.1.2动态代理 相比于静态代理来说动态代理更加灵活. 我们不需要针对每个目标对象都单独创建一个代理对象而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现.也就是说动态代理在程序运行时根据需要动态创建生成.比如房屋中介我不需要提前预测都有哪些业务而是业务来了我再根据情况创建.我们先看代码再来理解. Java也对动态代理进行了实现并给我们提供了一些API,常见的实现方式有两种 2.1.3JDK动态代理 JDK动态代理类实现步骤 1.定义一个接口及其实现类(静态代理中的HouseSubject和RealHouseSubject) 2.自定义InvocationHandler并重写invoke方法在invoke方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑 3.通过Proxy.newProxyInstance(ClassLoader loader,Class?[ ] interfaces,InvocationHandlerh)方法创建代理对象 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method; public class JDKInvocationHandler implements InvocationHandler { //⽬标对象即就是被代理对象private Object target; public JDKInvocationHandler(Object target) {this.target target;} Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 代理增强内容System.out.println(我是中介 , 开始代理); //通过反射调⽤被代理类的⽅法Object retVal method.invoke(target, args); //代理增强内容System.out.println(我是中介 , 代理结束);return retVal; }} 创建⼀个代理对象并使用 public class DynamicMain {public static void main(String[] args) {HouseSubject target new RealHouseSubject();//创建⼀个代理类通过被代理类、被代理实现的接口、⽅法调用处理器来创建HouseSubject proxy (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}} 1.InvocationHandler InvocationHandler接口是Java动态代理的关键接口之一它定义了一个单一方法invoke(),用于处理被代理对象的方法调用. public interface InvocationHandler {/*** 参数说明* proxy代理对象* method代理对象需要实现的⽅法即其中需要重写的⽅法* argsmethod所对应⽅法的参数*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;} 通过实现InvocationHandler接口可以对被代理对象的方法进行功能增强.2.Proxy Proxy类中使用频率最高的方法是newProxyInstance(),主要用来生成一个代理对象 public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)throws IllegalArgumentException{//...代码省略} 这个方法一共有3个参数 Loader:类加载器用于加载代理对象. interfaces:被代理类实现的一些接口(这个参数的定义也决定了JDK动态代理只能代理实现了接口的一些类) h:实现了InvocationHandler接口的对象 2.1.4CGLIB动态代理 JDK动态代理有一个最致命的问题是其只能代理实现了接口的类. 有些场景下我们的业务代码是直接实现的并没有接口定义.为了解决这个问题我们可以用CGLIB动态代理机制来解决. CGLIB(Code Generation Library)是一个基于ASM的字节码生成库它允许我们在运行时对字节码进行修改和动态生成.CGLIB通过继承方式实现代理很多知名的开源框架都使用到了CGLIB.例如Spring中的AOP模块中如果目标对象实现了接口则默认采用JDK动态代理否则采用CGLIB动态代理CGLIB动态代理类实现步骤 1.定义一个类(被代理类) 2.自定义MethodInterceptor并重写intercept方法intercept用于增强目标方法和JDK动态代理中的invoke方法类似 3.通过Enhancer类的create()创建代理类添加依赖 和JDK 动态代理不同, CGLIB(Code Generation Library) 实际是属于⼀个开源项⽬ 如果你要使用它的话 需要手动添加相关依赖 dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.3.0/version /dependency
⾃定义 MethodInterceptor方法拦截器 实现MethodInterceptor接口 public class CGLIBInterceptor implements MethodInterceptor {//⽬标对象, 即被代理对象private Object target; public CGLIBInterceptor(Object target){this.target target;} Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodPro // 代理增强内容System.out.println(我是中介 , 开始代理); //通过反射调⽤被代理类的⽅法Object retVal methodProxy.invoke(target, objects); //代理增强内容System.out.println(我是中介 , 代理结束);return retVal; }}
创建代理类, 并使用 public class DynamicMain {public static void main(String[] args) {HouseSubject target new RealHouseSubject();HouseSubject proxy (HouseSubject) Enhancer.create(target.getClass(),newproxy.rentHouse();}}
1.MethodInterceptor
MethodInterceptor和JDK动态代理中的InvocationHandler类似它只定义了一个方法intercept(),用于增强目标方法. public interface MethodInterceptor extends Callback {/*** 参数说明:* o: 被代理的对象* method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法) * objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法 */Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable; }
2. Enhancer.create() Enhancer.create()用来生成一个代理对象 public static Object create(Class type, Callback callback) { //...代码省略}
参数说明:
type: 被代理类的类型(类或接口)
callback: ⾃定义⽅法拦截器 MethodInterceptor
3.总结
1.AOP是一种思想是对某一类事情的集中处理.Spring框架实现了AOP,称之为SpringAOP 2.SpringAOP常见实现方式有两种1.基于注解Aspect来实现2.基于自定义注解来实现还有一些更原始的方式比如基于代理基于xml配置的方式但目标比较少见 3.SpringAOP是基于动态代理实现的有两种方式1.基本JDK动态代理实现2.基于CGLIB动态代理实现.运行时使用哪种方式与项目配置和代理的对象有关.