Spring 动态代理
时间:2022-07-24
本文章向大家介绍Spring 动态代理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、静态代理
静态代理要
- 要实现相同的接口;
- 要有原始对象;
- 要有额外的功能。
如下就是一个静态代理的实例。
public class UserServiceProxy implements UserService {
private UserServiceImpl userService = new UserServiceImpl();
@Override
public void addUser() {
userService.addUser();
System.out.println("UserServiceProxy.addUser");
}
@Override
public void deleteUser() {
userService.deleteUser();
System.out.println("UserServiceProxy.deleteUser");
}
}
静态代理最大的特点就是我们有一个原始类就要有一个代理类,静态的意思是代理类需要手工写出来一个源文件。
静态代理存在的问题:
- 类文件数量过多,不利于项目管理;
- 额外功能可维护性差,代理类中额外功能修改起来麻烦;
二、动态代理
Spring 动态代理
- 创建原始对象(目标对象); public class UserServiceImpl implements UserService { @Override public void register() { System.out.println("UserServiceImpl.register 业务运算 + DAO"); } @Override public boolean login() { System.out.println("UserServiceImpl.login"); return true; } } 将其添加到容器: <bean class="edu.lsu.service.impl.UserServiceImpl" id="userService"/>
- 提供额外功能;
Spring 提供了一个接口
MethodBeforeAdvice
,额外的功能书写在接口的实现中,会在原始的方法运行之前运行。 public class Before implements MethodBeforeAdvice { @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("Before.before---MethodBeforeAdvice"); } } <bean class="edu.lsu.dynamic.Before" id="before"/> - 定义切入点:额外功能加入的位置 目的:由程序员根据自己的需要,决定额外功能加入的位置。 <aop:config> <aop:pointcut id="pc" expression="execution(* *(..))"/> </aop:config>
- 组装 把切入点和额外的功能进行整合; <aop:config> <aop:pointcut id="pc" expression="execution(* *(..))"/> <!-- 所有的方法 都加入 before 的额外功能 --> <aop:advisor advice-ref="before" pointcut-ref="pc"/> </aop:config>
- 调用
目的:获得
Spring
工厂创建的动态代理对象,并进行调用;- Spring 的工厂通过原始对象的 id 值获得的是
代理对象
; - 可以使用接口类型存储代理对象。
- Spring 的工厂通过原始对象的 id 值获得的是
三、细节分析
Spring 创建的动态代理类在哪里?
Spring 框架在运行时,通过 动态字节码技术 ,在 JVM
创建时运行在 JVM
内部,等程序结束后会和 JVM
一起消失。
动态代理不需要定义类文件,都是 JVM 运行过程中动态创建的,所以不会造成 静态代理类文件数量过多影响项目管理 的问题。
动态代理的可维护性大大增强。
四、Spring 动态代理详解
MethodBeforeAdvice
我们通过实现 MethodBeforeAdvice
接口实现额外功能。
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("Before.before---MethodBeforeAdvice");
}
该接口有一个方法 before,他有 3 个参数:
- method:额外功能所增加给的那个原始方法;
- objects:原始方法的参数;
- o:代表额外功能所增加给的那个原始对象。
MethodInterceptor
它也叫作方法拦截器
这里使用的是 org.aopalliance.intercept.MethodInterceptor
, cjlib
包中也提供一个,但是我们不用那个。
该接口有一个 invoke
方法,重写该方法之后就能让额外功能执行在原始方法之前或者之后,或者之前和之后。
public class Around implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return null;
}
}
参数 MethodInvocation
:额外功能所增加给的那个原始方法,methodInvocation.proceed();
代表原始方法运行。
返回值代表原始方法执行后的返回值。
public class Around implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Around.invoke----原始方法之前运行.");
Object res = methodInvocation.proceed();
System.out.println("Around.invoke----原始方法之后运行.");
return res;
}
}
<bean class="edu.lsu.service.impl.UserServiceImpl" id="userService"/>
<bean class="edu.lsu.dynamic.Around" id="around"/>
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!-- 所有的方法 都加入 around 的额外功能 -->
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
</aop:config>
输出:
Around.invoke----原始方法之前运行.
UserServiceImpl.login
Around.invoke----原始方法之后运行.
Around.invoke----原始方法之前运行.
UserServiceImpl.register 业务运算 + DAO
Around.invoke----原始方法之后运行.
切入点表达式
* edu.lsu.service.*.*(..)
- 访问修饰符可以省略;
- 返回值可以使用通配符
*
表示任意返回值; - 包名可以是任意包,但是有几个包就写几个
*.
; -
*..
表示当前包及其子包。 - 参数类型可以使用通配符表示任意类型,可以使用
..
表示有无参数都行。
切入点函数
用于执行切入点表达式。
-
execution
它是最重要的切入点函数,功能最全,但是写法复杂。 -
args
用于函数(方法)参数的匹配 execution(* *(String, String)) == args(String, String) -
within
用于进行类、包切入点表达式的匹配 execution(* *..UserServiceImpl.*(..)) == within(*..UserServiceImpl) execution(* top.wsuo.proxy..*.*(..)) == within(top.wsuo.proxy..*) -
@annotation
为具有特殊注解的方法加入额外功能。 <aop:pointcut id="pc" expression="@annotation(edu.lsu.Log)"/> 将注解加到指定的方法之上就可以实现功能了。 - 切入点函数的逻辑运算
指的是整合多个切入点函数一起配合工作,进而完成更为复杂的需求。
-
and
与操作: execution(* login(String, String)) == execution(* login(..)) and args(String, String) -
or
或操作: execution(* login(..)) or execution(* login(..))
-
- Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T>
- Python进行数据可视化分析快速教程实例
- 一个抓取豆瓣图书的开源爬虫的详细步骤
- Java 8 Stream 教程 (三)
- silverlight ListBox 多列图片效果
- ZooKeeper构建分布式锁(选译)
- Caliburn.Micro学习笔记(二)----Actions
- 每天一个Linux命令:find
- textFile构建RDD的分区及compute计算策略
- 一个基于Flask和MongoDB的CMS内容管理系统
- c++多重继承小结
- 初识PB级数据分析利器Prestodb
- 使用python实现RESTful API服务器端的思路
- c++ this *this
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释