目录
一, 什么是代理
二, 静态代理
三, 动态代理
1. 使用 Proxy 实现动态代理
2. 动态代理的优势
3.proxy 动态代理的局限性
四, cglib 动态代理
五, 总结
一, 什么是代理
假如有一个目标对象 A, 使用者想使用 A 中的方法, 它可以先访问一个代理对象 B, 再由 B 去访问 A, 此时 B 对象就是 A 的代理; 这时可以在不改变 A 源码的情况下, 通过 B 来增强 A 中方法的功能. 其中的 A 类被称为委托类, B 类被称为代理类.
代理的实现方式有两种, 静态代理和动态代理.
二, 静态代理
静态代理实际上是创建了一个代理类, 传入原始的委托类的对象, 在代理类的内部先执行增强的功能, 再执行委托类的方法. 为了保持功能的一致, 委托类和代理类会实现同一个接口, 以下以一个 UserService 的场景举例.
假设存在一个 IUserService 接口, 其中有对用户相关操作的方法
- package com.lyy.service;
- /**
- * UserService 接口, 提供用户相关的操作
- */
- public interface IUserService {
- void addUser(String username);
- void deleteUser(String userId);
- }
UserServiceImpl 是实现类
- package com.lyy.service.impl;
- import com.lyy.service.IUserService;
- public class UserServiceImpl implements IUserService {
- @Override
- public void addUser(String username) {
- System.out.println("增加用户:"+username);
- }
- @Override
- public void deleteUser(String userId) {
- System.out.println("删除用户:"+userId);
- }
- }
现在创建一个 UserServiceImpl 的代理类来增强其功能, 比如在方法的执行前后增加日志输出的功能
- package com.lyy.service.proxy;
- import com.lyy.service.IUserService;
- /**
- * UserService 静态代理类
- */
- public class UserServiceLogProxy implements IUserService {
- private IUserService userService;
- // 通过构造方法传入委托类的对象
- public UserServiceLogProxy(IUserService userService){
- this.userService=userService;
- }
- // 给委托类的方法执行前后增加日志输出的功能
- @Override
- public void addUser(String username) {
- System.out.println("addUser 开始执行");
- userService.addUser(username);
- System.out.println("addUser 执行完成");
- }
- // 给委托类的方法执行前后增加日志输出的功能
- @Override
- public void deleteUser(String userId) {
- System.out.println("deleteUser 开始执行");
- userService.deleteUser(userId);
- System.out.println("deleteUser 执行完成");
- }
- }
最后测试通过代理对象来执行委托类的方法
- /**
- * 测试通过静态代理来增强委托类方法的功能
- */
- @Test
- public void test1(){
- // 创建委托类的对象
- IUserService userService=new UserServiceImpl();
- // 创建代理类的对象
- UserServiceLogProxy userServiceLogProxy=new UserServiceLogProxy(userService);
- // 通过代理类的对象来执行委托类的方法
- userServiceLogProxy.addUser("张三");
- System.out.println("-----------------");
- System.out.println("-----------------");
- userServiceLogProxy.deleteUser("123");
- }
执行的结果如下图
以上就是使用静态代理来增强委托类方法的功能的举例. 现在对这种方式的优缺点进行分析
优点: 在不改变 UserServiceImpl 源码的前提下增强了其中方法的功能
缺点: 委托类和代理类实现了同样的接口, 有较多的代码重复; 一个代理类只能对一个委托类的方法进行增强, 如果需要对另一个 Service 实现类也添加前后日志记录的功能, 则必须再创建一个代理类, 这个代理类的代码不能复用.
三, 动态代理
动态代理的动态是相对于静态代理来说的. 对于静态代理, 代理类是在编码时就完成的, 在程序运行前就生成了对应的字节码文件; 而动态代理, 是在程序运行时才动态的生成代理类的字节码文件.
java 中实现动态代理有两种方式, Proxy 和 cglib
1. 使用 Proxy 实现动态代理
如果一个类至少实现了一个接口, 就可以使用 jdk 自带的 Proxy 来动态的增强其方法的功能
(1) 创建一个动态代理类
- package com.lyy.service.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- /**
- * 使用动态代理来增强方法的功能, 给方法的前后增加日志
- * 代理类需要实现 InvocationHandler 接口, 重写 invoke 方法
- */
- public class LogServiceProxy implements InvocationHandler {
- private Object targetObj;// 委托类的对象
- public LogServiceProxy(Object targetObj){
- this.targetObj=targetObj;
- }
- /**
- * 重写该方法, 在其中完成委托类方法功能的增强
- * @param proxy
- * @param method
- * @param args
- * @return
- * @throws Throwable
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println(method.getName()+", 开始执行");
- method.invoke(targetObj,args);// 使用反射来执行委托类中的方法
- System.out.println(method.getName()+", 执行成功");
- return null;
- }
- }
(2) 使用该代理类
- /**
- * 测试用动态代理来增强委托类的方法功能
- */
- @Test
- public void test2(){
- // 创建委托类的对象
- IUserService userService=new UserServiceImpl();
- // 创建代理类的对象
- LogServiceProxy logServiceProxy=new LogServiceProxy(userService);
- // 创建功能增强后的委托类对象, 用委托类实现的接口作为类型来接收
- IUserService userServiceProxy= (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
- userService.getClass().getInterfaces(),logServiceProxy);
- // 通过增强对象来执行方法
- userServiceProxy.addUser("李四");
- System.out.println("-----------------");
- System.out.println("-----------------");
- userServiceProxy.deleteUser("456");
- }
结果如图
使用过程主要分为如下几步:
创建为委托类的对象
创建代理类的对象并传入委托类的对象
通过 Proxy 得到功能被增强的委托类对象, 用它实现的接口类型来接收
通过增强对象执行方法
Proxy.newProxyInstance(1,2,3) 方法的 3 个参数介绍
1, 传入委托类的类加载器
2, 传入委托类实现的接口数组
3, 代理类的对像
2. 动态代理的优势
以上使用动态代理的 LogServiceProxy 代理类, 不仅可以用来增强 UserService 的功能, 还可以用来增强其他 Service 的功能, 是可以复用的. 比如有一个 BolgServcie
IBlogService 接口
- package com.lyy.service;
- /**
- * BlogService 接口
- */
- public interface IBlogService {
- void addBlog(String name);
- }
BlogServiceImpl 实现类
- package com.lyy.service.impl;
- import com.lyy.service.IBlogService;
- public class BlogServiceImpl implements IBlogService {
- @Override
- public void addBlog(String name) {
- System.out.println("增加博客:"+name);
- }
- }
使用 LogServiceProxy 来增强其功能
- /**
- * 使用动态代理增强 BlogService 的功能
- */
- @Test
- public void test3(){
- // 创建委托类的对象
- IBlogService blogService=new BlogServiceImpl();
- // 创建代理类的对象
- LogServiceProxy logServiceProxy=new LogServiceProxy(blogService);
- // 创建功能增强后的委托类对象, 用委托类实现的接口作为类型来接收
- IBlogService proxyBlogService= (IBlogService) Proxy.newProxyInstance(blogService.getClass().getClassLoader(),
- blogService.getClass().getInterfaces(),logServiceProxy);
- // 通过增强对象来执行方法
- proxyBlogService.addBlog("动态代理学习");
- }
运行结果如下图
说明这个代理类是可以复用的
3.proxy 动态代理的局限性
使用 jdk 自带的 proxy 实现动态代理有一个限制, 就是委托类最少要实现一个接口, 否则不能使用这种方式.
四, cglib 动态代理
使用第三方提供的 cglib 也可以实现动态代理. 该方式不要求委托类一定要实现接口, 但它要求委托类不能是最终类, 即要能够被继承.
使用 maven 构建工程时 cglib 的 maven 依赖如下
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib</artifactId>
- <version>2.2.2</version>
- </dependency>
创建一个代理类, 实现 MethodInterceptor 接口
- package com.lyy.service.proxy;
- import.NET.sf.cglib.proxy.MethodInterceptor;
- import.NET.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
- /**
- * 使用 cglib 动态代理, 实现 service 方法的日志功能增强
- */
- public class LogServiceByCglib implements MethodInterceptor {
- private Object targetObject;
- // 使用构造方法传入委托类的对象
- public LogServiceByCglib(Object targetObject){
- this.targetObject=targetObject;
- }
- // 通过这个方法增强委托类的功能
- @Override
- public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- System.out.println(method.getName()+", 开始执行");
- method.invoke(targetObject,args);
- System.out.println(method.getName()+", 执行成功");
- return null;
- }
- }
在测试类中测试, 分别测试用 cglib 增强有实现接口的类和没有实现接口的类的方法
测试增强 blogService 的功能
- /**
- * 测试使用 cglib 来动态增强 service 的功能
- */
- @Test
- public void test4(){
- // 创建委托类的对象
- IBlogService blogService=new BlogServiceImpl();
- // 创建代理类的对象
- LogServiceByCglib logServiceByCglib=new LogServiceByCglib(blogService);
- // 创建功能增强后的委托类对象, 用委托类实现的接口作为类型来接收 (也可以直接用委托类来接收)
- IBlogService proxyBlogService = (IBlogService) Enhancer.create(blogService.getClass(), logServiceByCglib);
- // 通过增强对象来执行方法
- proxyBlogService.addBlog("cglib 动态代理");
- }
新建一个没有实现接口的 service
- package com.lyy.service;
- /**
- * 测试 cglib 实现动态代理, 该 service 类没有实现接口
- */
- public class AccountService {
- public void addAccount(String name){
- System.out.println("添加账户:"+name);
- }
- }
测试增强该类中方法的功能
- /**
- * 测试用 cglib 来增强没有实现接口的类中方法的功能
- */
- @Test
- public void test5(){
- // 创建委托类的对象
- AccountService accountService=new AccountService();
- // 创建代理类的对象
- LogServiceByCglib logServiceByCglib=new LogServiceByCglib(accountService);
- // 创建功能增强后的委托类对象, 直接用委托类来接收
- AccountService proxyAccountService = (AccountService) Enhancer.create(accountService.getClass(), logServiceByCglib);
- // 执行方法
- proxyAccountService.addAccount("cglib 增强没有实现接口的类");
- }
五, 总结
静态代理和动态代理都可以在不修改委托类源码的基础上增强其中方法的功能, 但动态代理更具有通用性, 不需要有一个 service 就创建一个代理类.
动态代理有两种实现方式, 各自都有自己的使用要求,
proxy: 要求委托类至少实现一个接口
cglib: 要求委托类不能是最终类, 即该类是可以被其他类继承的.
来源: http://www.bubuko.com/infodetail-3266933.html