加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java 代理

发布时间:2020-12-15 07:59:00 所属栏目:Java 来源:网络整理
导读:? 代理(proxy)分为2种: 静态代理 动态代理? ? 动态代理常用的有jdk动态代理、cglib代理。 ? ? 静态代理 1、新建User接口 1 public interface User { 2 void addUser(); 3 void deleteUser(); 4 void updateUser(); 5 } ? ? 2、新建实现类UserImpl 1 publi

?

代理(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,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,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,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。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读