1.1. 代理模式概述
1.1.1. 定义
代理模式,即为目标对象提供了一种机制,用于控制对目标对象的访问。在某些情况下,一个对象不适合或者不能直接引用某个对象,则可以考虑使用代理对象,代理对象在客户端和目标对象之间起到中介的作用。
1.1.2. 代理模式优点
l 职责清晰:真实角色(目标对象)只需要关注核心业务逻辑,不需要关心其它非本质的事务。非本质事务通过后期代理对象来完成。
l 安全性好:代理对象,在客户端和目标对象之间起到中介的作用。实现了对目标对象的保护。
l 扩展性好:在运行时实现对目标对象功能的增强,属于字节码增强,不需要修改目标对象java源代码。
1.2. 动态代理两种实现方式
1.2.1. 基于接口的动态代理
提供者:jdk
要求:被代理类,至少要实现一个接口。
1.2.2. 基于子类的动态代理
提供者:第三方的cglib
要求:被代理类不能是使用final修饰的类。即不能是最终类。
1.3. 动态代理案例演示
1.3.1. 案例需求
我们此处以演员、剧组、经纪人作为案例程序。在很久很久以前,演艺圈是没有经纪人行业的,都是剧组直接跟演员联系。后来逐渐有了经纪公司(经纪人),专业为演员和剧组提供专业服务,释放演员的劳动力,让演员可以更加专注于演好戏。
JDK的方式
1 /** 2 * 演员接口 3 */ 4 public interface Actor { 5 6 /** 7 * 基本表演 8 */ 9 void basicAct(Float money); 10 11 /** 12 * 高难度表演 13 */ 14 void difficultyAct(Float money); 15 16 /** 17 * 演员名称 18 */ 19 String getActName(); 20 }
1 /** 2 * 演员实现类 3 */ 4 public class ActionActor implements Actor { 5 6 /** 7 * 基本表演 8 */ 9 public void basicAct(Float money) { 10 System.out.println("演员【"+getActName()+"】收到钱:"+money+",开始基本表演。"); 11 } 12 13 /** 14 * 高难度表演 15 */ 16 public void difficultyAct(Float money) { 17 System.out.println("演员【"+getActName()+"】收到钱:"+money+",开始高难度表演。"); 18 } 19 20 /** 21 * 演员名称 22 */ 23 public String getActName() { 24 return "小明"; 25 } 26 }
1 /** 2 * 2.剧组通过经纪人,找演员表演 3 * 4 * 实现方式: 5 * jdk的动态代理 6 * 涉及到的类: 7 * Proxy 8 * 涉及到的方法: 9 * newProxyInstance() 10 * 方法参数: 11 * Classloader:类加载器。与被代理类使用相同的类加载器。固定写法 12 * interfaces:接口列表。与被代理类实现相同的接口列表。固定写法 13 * InvocationHandler:如何代理。这里实现增强功能 14 */ 15 // 创建被代理对象(演员) 16 final Actor actor = new ActionActor(); 17 // 创建代理对象(经纪人) 18 Actor actorProxy = (Actor) Proxy.newProxyInstance( 19 actor.getClass().getClassLoader(), 20 actor.getClass().getInterfaces(), 21 22 new InvocationHandler() { 23 /** 24 *增强功能方法:invoke() 25 * 参数列表: 26 * proxy:代理对象引用。一般用不到 27 * method:当前执行的方法对象 28 * args:当前执行的方法参数列表 29 */ 30 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 31 32 // 1.剧组找到经纪人 33 // 1.1.明确表演需求:方法名称 34 String methodName = method.getName(); 35 36 // 1.2.明确出场费用 37 Float money = (Float) args[0]; 38 39 // 2.经纪人联系演员:是基本表演?还是高难度表演?给多少钱? 40 System.out.println("经纪人正在联系演员。"); 41 42 // 2.1.是基本表演吗?基本表演要求5000块 43 Object retV = null; 44 if("basicAct".equals(methodName)){ 45 if(money >= 5000){ 46 System.out.println("基本表演,剧组钱给到位了。演员收下这个剧本。"); 47 // 开始表演 48 retV = method.invoke(actor, money * 4 / 5); 49 }else{ 50 System.out.println("剧组只给了:"+money+",钱不到位。演员表示档期太满。"); 51 } 52 } 53 54 // 2.2.是高难度表演吗?高难度表演要求10000块 55 if("difficultyAct".equals(methodName)){ 56 if(money >= 10000){ 57 System.out.println("高难度表演,剧组钱给到位了。演员收下这个剧本。"); 58 retV = method.invoke(actor,money*4/5); 59 }else{ 60 System.out.println("剧组只给了:"+money+",钱不到位。演员直接表示拒绝这个剧本。"); 61 } 62 } 63 64 return retV; 65 } 66 } 67 ); 68 69 // 经纪人代理演员,开始表演 70 // 基本表演 71 actorProxy.basicAct(5000f); 72 73 System.out.println("-------------华丽丽分割线---------------"); 74 75 // 高难度表演 76 actorProxy.difficultyAct(10000f); 77 }
cglib的方式
导入cglib依赖包
<packaging>jar</packaging> <properties> <!--cglib版本--> <cglib.version>2.1_3</cglib.version> </properties> <dependencies> <!--cglib包--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib.version}</version> </dependency> </dependencies>
1 /** 2 * 目标对象:演员 3 */ 4 public class ArtActor { 5 6 /** 7 * 基本表演 8 */ 9 public void basicAct(Float money) { 10 System.out.println("演员【"+getActorName()+"】收到钱:"+money+",开始基本表演。"); 11 } 12 13 /** 14 * 高难度表演 15 */ 16 public void difficultyAct(Float money) { 17 System.out.println("演员【"+getActorName()+"】收到钱:"+money+",开始高难度表演。"); 18 } 19 20 /** 21 * 演员名称 22 */ 23 public String getActorName(){ 24 return "小花"; 25 } 26 }
1 * 实现方式: 2 * 第三方的cglib动态代理实现 3 * 涉及到的类: 4 * EnHancer 5 * 涉及到的方法: 6 * create() 7 * 参数: 8 * Class:被代理类的字节码 9 * Callback:如何代理。这里是实现增强的地方。相当于InvocationHandler 10 */ 11 // 创建目标对象(演员) 12 final ArtActor actor = new ArtActor(); 13 14 // 创建代理对象(经纪人) 15 ArtActor actorProxy = (ArtActor)Enhancer.create( 16 actor.getClass(), 17 18 new MethodInterceptor() { 19 /** 20 *实现增强功能方法:intercept()。相当于invoke() 21 * 参数: 22 * proxy:代理对象引用。一般用不到 23 * method:当前执行的方法对象 24 * args:当前执行方法的参数列表 25 * methodProxy:当前方法对象的代理引用。一般用不到 26 */ 27 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 28 29 // 1.剧组找到经纪人 30 // 1.1.明确表演需求:方法名称 31 String methodName = method.getName(); 32 33 // 1.2.明确出场费用 34 Float money = (Float) args[0]; 35 36 // 2.经纪人联系演员:是基本表演?还是高难度表演?给多少钱? 37 System.out.println("经纪人正在联系演员。"); 38 39 // 2.1.是基本表演吗?基本表演要求5000块 40 Object retV = null; 41 if("basicAct".equals(methodName)){ 42 if(money >= 5000){ 43 System.out.println("基本表演,剧组钱给到位了。演员收下这个剧本。"); 44 // 开始表演 45 retV = method.invoke(actor, money * 4 / 5); 46 }else{ 47 System.out.println("剧组只给了:"+money+",钱不到位。演员表示档期太满。"); 48 } 49 } 50 51 // 2.2.是高难度表演吗?高难度表演要求10000块 52 if("difficultyAct".equals(methodName)){ 53 if(money >= 10000){ 54 System.out.println("高难度表演,剧组钱给到位了。演员收下这个剧本。"); 55 retV = method.invoke(actor,money*4/5); 56 }else{ 57 System.out.println("剧组只给了:"+money+",钱不到位。演员直接表示拒绝这个剧本。"); 58 } 59 } 60 61 return retV; 62 } 63 } 64 ); 65 66 // 经纪人代理演员,开始表演 67 // 基本表演 68 actorProxy.basicAct(5000f); 69 70 System.out.println("----------------华丽丽分割线----------------"); 71 72 // 高难度表演 73 actorProxy.difficultyAct(10000f); 74 75 }
1.1. AOP介绍
AOP(Aspect Oriented Programming),即面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件系统开发中的一个热点,也是spring框架的一个重点。利用AOP可以实现业务逻辑各个部分的隔离,从而使得业务逻辑各个部分的耦合性降低,提高程序的可重用性,同时提高开发效率。
简单理解:aop是面向切面编程,是使用动态代理技术,实现在不修改java源代码的情况下,运行时实现方法功能的增强。
1.2. AOP的作用及优势
1.2.1. 作用
使用动态代理技术,在程序运行期间,不修改java源代码对已有方法功能进行增强。
1.2.2. 优势
l 减少重复代码,提高开发效率。
l 统一管理统一调用,方便维护。
1.3. AOP实现技术
动态代理技术。
1.4. AOP常用术语【掌握】
l Joinpoint(连接点):在spring中,连接点指的都是方法(指的是那些要被增强功能的候选方法),spring只支持方法类型的连接点。
l Pointcut(切入点):在运行时已经被spring 的AOP实现了增强的方法。
l Advice(通知):通知指的是拦截到Joinpoint之后要做的事情。即增强的功能。通知类型:前置通知/后置通知/异常通知/最终通知/环绕通知。
l Target(目标对象):被代理的对象。比如动态代理案例中的演员。
l Weaving(织入):织入指的是把增强用于目标对象,创建代理对象的过程。spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入。
l Proxy(代理):一个类被AOP织入增强后,即产生一个结果代理类。比如动态代理案例中的经纪人。
l Aspect(切面):切面指的是切入点和通知的结合。
1.5. 学习spring的AOP需要明确的事情【掌握】
1.5.1. 开发阶段(我们做的)
l 根据业务需求,编写核心业务代码
l 把公用代码抽取出来,制作成通知
l 通过配置的方式,建立切入点(业务功能)和通知的关系
1.5.2. 运行阶段(spring框架完成)
spring框架监控切入点方法执行。一旦监控到切入点方法被执行,使用动态代理机制,创建目标对象的代理对象,根据通知类型,在代理对象当前执行方法的对应位置,织入通知功能,完成完整的代码逻辑执行。
二、spring的AOP概述
1.AOP定义:AOP是面向切面编程,底层使用动态代理技术,在不修改目标java源代码的情况下,
实现方法功能的增强。
2.使用spring的AOP:
开发阶段需要明确事情:
第一步:根据业务需求,编写业务的功能代码
第二步:把公共的代码,抽取出来,制作成通知(增强的功能)
第三步:通过配置方式,建立业务功能代码,与通知的关系
三、基于xml的AOP配置
实现步骤:
第一步:创建项目
第二步:配置pom.xml,导入jar包
导入ioc的jar包
导入aop的jar包
第三步:根据业务需求编写业务代码
第四步:把公共代码抽取出来制作成通知
第五步:通过配置的方式,建立业务功能与通知的关系
第一步:导入aop的名称空间
第二步:配置aop步骤
通过<aop:config/>声明aop配置
通过<aop:aspect/>配置切面
通过<aop:before/>配置前置通知
通过<aop:pointcut/>配置切入点表达式
切入点表达式演化(aspectj表达式):
表达式组成部分:访问修饰符 返回值类型 包名称 类名称 方法名称 (参数列表)
通知类型:
前置通知:在目标方法执行前执行
后置通知:在目标方法正常返回后执行。它和异常通知只能出现一个
异常通知:在目标方法发生异常后执行。它和后置通知只能出现一个
最终通知:无论目标方法正常返回,还是发生异常,都可以执行
环绕通知:
四、基于注解和xml的AOP配置
实现思路:
第一步:把我们编写的客户service和通知advice使用注解进行配置
第二步:在bean.xml配置包扫描service和advice
第三步:把aop的配置内容使用注解进行配置
第四步:开始spring对注解AOP的支持【关键步骤】
五、纯注解的AOP配置【了解】
思路:编写一个java类,作为spring的配置类,代替bean.xml文件
实现步骤:
第一步:编写spring的配置类
第二步:在controller中,加载spring配置类