Java 代理
2019-08-26 06:18:39来源:博客园 阅读 ()
Java 代理
代理(proxy)分为2种:
- 静态代理
- 动态代理 动态代理常用的有jdk动态代理、cglib代理。
静态代理
1、新建User接口
1 public interface User { 2 void addUser(); 3 void deleteUser(); 4 void updateUser(); 5 }
2、新建实现类UserImpl
1 public class UserImpl implements User { 2 @Override 3 public void addUser() { 4 //模拟添加用户 5 System.out.println("正在添加用户"); 6 System.out.println("已添加用户"); 7 } 8 9 @Override 10 public void deleteUser() { 11 //模拟删除用户 12 System.out.println("正在删除用户"); 13 System.out.println("已删除用户"); 14 } 15 16 @Override 17 public void updateUser() { 18 //模拟修改用户信息 19 System.out.println("正在修改用户信息"); 20 System.out.println("已修改用户信息"); 21 } 22 }
3、新建代理类UserProxy,也实现User接口,对目标对象(的方法)进行增强
1 public class UserProxy implements User { 2 //定义目标对象(要代理的对象) 3 private User user; 4 5 //构造函数,初始化目标对象 6 public UserProxy(User user){ 7 this.user=user; 8 } 9 10 @Override 11 public void addUser() { 12 System.out.println("开启事务..."); //前处理,模拟开启事务 13 user.addUser(); //调用目标对象相应的方法 14 System.out.println("提交事务..."); //后处理,模拟提交事务 15 } 16 17 @Override 18 public void deleteUser() { 19 System.out.println("开启事务..."); 20 user.deleteUser(); 21 System.out.println("提交事务..."); 22 } 23 24 @Override 25 public void updateUser() { 26 System.out.println("开启事务..."); 27 user.updateUser(); 28 System.out.println("提交事务..."); 29 } 30 }
4、使用代理。新建测试类Test
1 public class Test { 2 public static void main(String[] args){ 3 UserImpl user = new UserImpl(); //目标对象 4 UserProxy userProxy = new UserProxy(user); //代理 5 userProxy.addUser(); //调用代理类的方法 6 } 7 }
静态代理的特点
- 代理类、目标类需要实现同一接口
- 编译时就已确定目标对象的类型(编译时类型,比如上例中编译时就知道目标对象的类型是User)
- 目标对象只能必须是特定类型,比如上例中目标对象只能是User类型,UserProxy这个代理类只能代理User的实例。如果要代理其他类型的对象,需要再写代理类,这就很麻烦了,一种代理只能代理一种类型,太鸡肋了,我们一般不用静态代理。
因为目标对象的类型是固定的,静止不动的(静态的),所以这种代理方式叫做静态代理。
何谓代理?
代理是使用一个更强大的类(在原类的基础上进行功能扩展)来代替原来的类进行工作。
比如我在使用UserImpl类时,还想使用事务、记录日志等做一些其他的操作,这些操作不属于用户类的范畴,不能封装到UserImpl类中。这时就可以使用代理来对原来的类(方法)进行增强。代理类保留了原有类的所有功能,在此基础上扩展了其他功能,更加强大。
被代理的类(UserImpl类)叫做目标类,实现了代理的类(UserProxy类)叫做代理类。
JDK动态代理
1、新建接口User
2、新建实现类UserImpl
3、新建类ProxyFactory,用于创建代理对象
1 class ProxyFactory{ 2 //目标对象。动态代理是你传给它什么目标对象,它就代理什么对象,能代理所有类型的对象,所以声明为Object 3 private Object target; 4 5 //构造器,初始化目标对象 6 public ProxyFactory(Object target) { 7 this.target = target; 8 } 9 10 //获取代理对象。目标对象是Object,所以代理对象也是Object 11 public Object getProxyInstance(){ 12 ClassLoader classLoader = target.getClass().getClassLoader(); //获取目标类的类加载器。可通过目标对象获取,也可通过目标类获取,但目标对象声明为Object,所以只能通过目标对象来获取(不知道目标类) 13 Class<?>[] interfaces = target.getClass().getInterfaces(); //获取目标类所实现的所有接口的Class对象。因为目标类可能实现了多个接口,所以用的是复数。 14 InvocationHandler invocationHandler = new InvocationHandler() { //创建InvocationHandler接口的实例。这里使用匿名内部类来创建 15 @Override 16 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 17 System.out.println("前增强..."); //此处写前增强的代码。 18 Object returnValue=method.invoke(target,args); //调用目标方法 19 System.out.println("后增强..."); //此处写后增强的代码 20 return returnValue; 21 } 22 }; 23 24 25 Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); //创建代理对象,参数就是上面三个参数 26 return proxyInstance; //返回代理对象 27 } 28 }
4、新建测试类Test
1 public class Test { 2 public static void main(String[] args){ 3 UserImpl user = new UserImpl(); //目标对象 4 Object obj = new ProxyFactory(user).getProxyInstance(); //创建代理对象 5 User userProxy=(User)obj; //返回值是Object,需要强转。只能强转为目标类所实现的接口类型,如果强转为目标类的类型则代理失败(报错) 6 userProxy.addUser(); //通过代理对象来调用方法 7 } 8 }
我们看下实现 InvocationHandler接口中的 这段代码:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前增强..."); //此处写前增强的代码 Object returnValue=method.invoke(target,args); //调用目标方法 System.out.println("后增强..."); //此处写后增强的代码 return returnValue; }
invocation是调用的意思,invoke也是调用的意思。InvocationHandler接口是用来指定如何调用目标类中的方法,按照写的实现来调用。
invoke这个函数就是反射中调用指定方法的函数。
java.lang.reflect中调用某个类中的方法: Object invoke(Object proxy, Method method, Object[] args)
第一个参数指定目标对象,第二个参数指定要调用的方法,第三个参数是所调方法需要的实参值,参数个数不确定,可写为数组形式。invoke()的返回值就是所调方法的返回值,只不过声明为了Object。
通过代理对象调用方法: userProxy.addUser();
会自动把目标对象、要调用的方法、实参表向下传递给invoke(),invoke执行完以后自动把返回值向上传回来。
jdk动态代理,是指用jdk自带的东西(反射)来实现动态代理,所以又叫java动态代理,不是说要代理jdk。
前后增强的代码可以放在函数中,然后在invoke()中调用对应的函数。
JDK动态代理的特点
- 目标类必须继承接口,创建代理的类无需继承接口
- 可以代理任何类型的类。传什么类型的目标类,就代理什么类型,所有叫做动态代理。
- 所设置的前后增强,是所有目标类中所有方法都会添加(执行)的。静态代理可以一个方法一个方法地设置,可以根据需要具体设置每一个方法的增强。jdk动态代理是一棍子打死,全都使用相同的前后增强。
CGLIB动态代理
静态代理、jdk动态代理都要求目标类必须实现一个或多个接口,如果目标类没有实现接口,则代理失败。
cglib代理不要求目标类实现接口。
1、如果是单独使用cglib,需要导入cglib.jar包以及cglib的依赖包asm.jar,不推荐,这样导包很容易出问题(cglib、asm的版本要对应)。
如果使用maven,则会自动导入依赖的asm.jar。
如果使用了spring,在spring的核心包spring-core.jar中已经内嵌了cglib,不必再导包。
2、新建类User,不必实现任何接口
3、新建创建代理对象的类ProxyFactory,需实现MethodInterceptor接口
1 class ProxyFactory implements MethodInterceptor { 2 //目标对象。声明为Object 3 private Object target; 4 5 //构造器,初始化目标对象 6 public ProxyFactory(Object target) { 7 this.target = target; 8 } 9 10 //给目标对象创建一个代理对象 11 public Object getProxyInstance(){ 12 Enhancer en = new Enhancer(); //创建工具类对象 13 en.setSuperclass(target.getClass()); //设置父类 14 en.setCallback(this); //设置回调函数。这句代码是给代理类对象设置拦截intercept()。前面只是继承了目标类,此处设置拦截(在拦截中实现增强、调用目标方法) 15 return en.create(); //创建代理对象并返回 16 17 } 18 19 //拦截 20 @Override //cglib代理仍是在反射的基础上扩展而来的,所以参数和反射调用方法的参数差不多目标对象、背调方法、实参表、方法代理 21 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 22 System.out.println("前增强..."); //前增强代码 23 Object returnValue = method.invoke(target, objects); //传入目标对象、实参表。 24 System.out.println("后增强..."); //后增强代码 25 return returnValue; //返回被调函数的返回值,是作为Object返回的 26 } 27 28 }
4、新建测试类Test
1 public class Test { 2 public static void main(String[] args){ 3 User user = new User(); //目标对象 4 Object obj = new ProxyFactory(user).getProxyInstance(); //创建代理对象 5 User userProxy=(User) obj; //返回值是Object,需要强转为目标类对象。 6 userProxy.addUser(); //通过代理对象来调用方法 7 } 8 }
CGLIB动态代理的特点
- 可以代理任何类,但目标类不能用final修饰,因为代理类要继承目标类;目标类中的方法不能使用final(要改写,前后增强)、static(要求是实例方法)修饰。
- 目标类可以实现接口,也可以不实现任何接口。
- 目标类中的所有方法都会被增强
如果目标类实现了接口,使用jdk、cglib都行;如果目标类没有实现任何接口,则使用cglib。
原文链接:https://www.cnblogs.com/chy18883701161/p/11391061.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- 代理项 Surrogate 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash