java代理

代理

1.       程序中的代理:要为已存的多个具有相同功能接口的目标类的各个方法增加一些系统功能,例如:异常处理,日志,计算方法和运行时间。

2.       编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用功能上增加系统功能代码。

3.       系统中存在交叉业务,一个交叉业务就是要切入到系统的一个方面。

a)       交叉业务的编程问题即为面向方面的编程问题(Aspectoriented program)简称AOP,AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的。

4.       动态代理技术:

a)       要为系统中的各种借口类增加代理功能,那就需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事,JVM可以再运行时期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理。

b)      JVM生成的的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能作用于具有相同接口的目标类的代理。

c)       CGLIB库可以动态生成一个类的子类,一个类的子类也可以作该类的代理,所以,要为一个没有实现接口的类生成动态代理,那么可以使用CGLIB库。

d)      代理的各个方法通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

l         在调用方法之前

l         在调用方法之后

l         在调用方法前后

l         在处理目标方法异常的catch块中

5.       代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

6.       要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:

a)       java.lang.reflect.Proxy:这是 Java动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象

l         1: 该方法用于获取指定代理对象所关联的调用处理器

l         static InvocationHandler getInvocationHandler(Objectproxy)

l          

l         2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象

l         static Class getProxyClass(ClassLoader loader, Class[]interfaces)

l          

l         3:该方法用于判断指定类对象是否是一个动态代理类

l         static boolean isProxyClass(Class cl)

l          

l         4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例

l         static Object newProxyInstance(ClassLoader loader,Class[] interfaces,

l             InvocationHandler h)

b)       java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。 

l         // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象

l         // 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行

l         Object invoke(Object proxy, Method method, Object[] args)

c)        java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象

7.        Java 动态代理。具体有如下四步骤:

a)       通过实现 InvocationHandler接口创建自己的调用处理器;

b)       通过为 Proxy类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

c)        通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

d)       通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

8动态代理对象创建过程

a)       //InvocationHandlerImpl 实现了InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

b)       // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

c)        InvocationHandlerhandler = new InvocationHandlerImpl(..);

d)        

e)       // 通过 Proxy 为包括Interface 接口在内的一组接口动态创建代理类的类对象

f)        Classclazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

g)        

h)       // 通过反射从生成的类对象获得构造函数对象

i)         Constructorconstructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

j)          

k)       // 通过构造函数对象创建动态代理类实例

l)         InterfaceProxy = (Interface)constructor.newInstance(new Object[] { handler });

9     分析JVM动态生成的类

创建实现了Collection接口的动态类和查看其名称,分析Proxy和getProxyClass方法的各个参数。

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Collection;

 

 

public class ProxyText {

    public static void main(String[] args)throws Exception {

       Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

       //获取Proxy这份字节码,实现了Collection接口,类加载器为对应的接口类的类加载器

       System.out.println(clazzProxy1.getName()+"-------");

      

       System.out.println("-----------BeginConstructor List-------------");

       Constructor[] constructors =clazzProxy1.getConstructors();

       //获取字节码的所有构造函数,存到数组中

       for(Constructor constructor : constructors){

           //高级for循环遍历constructors

           String name = constructor.getName();

           StringBuilder sBuilder = new StringBuilder(name);

           //定义容器存储名称

           sBuilder.append('(');

           Class[] clazzParams = constructor.getParameterTypes();

           //clazzParams储存参数类型,

           for(Class clazzParam : clazzParams){

              sBuilder.append(clazzParam.getName()).append(',');

              //在取出每个类型的名字

           }

           if(clazzParams!=null && clazzParams.length!=0)

              sBuilder.deleteCharAt(sBuilder.length()-1);

              sBuilder.append(')');

           System.out.println(sBuilder);

       }

       System.out.println("-----------BeginMethod List-------------");

       Method[] Methods = clazzProxy1.getMethods();

       //取出字节码所有的方法,

       for(Method Method : Methods){

       String name = Method.getName();

        //每个方法名字

       StringBuilder sBuilder = new StringBuilder(name);

       sBuilder.append('(');

       Class[] clazzParams = Method.getParameterTypes();

       //方法的参数类型

       for(Class clazzParam : clazzParams){

           sBuilder.append(clazzParam.getName()).append(',');

       }

       if(clazzParams!=null && clazzParams.length!=0)

           sBuilder.deleteCharAt(sBuilder.length()-1);

           sBuilder.append(')');

       System.out.println(sBuilder);

       }

总结:动态代理类$Proxy0内部有带一个参数(InvocationHandler)的构造方法,

Proxy0实现了COnection接口,不仅得到了Connection的方法还获取了ObjectHashCodetoString方法

System.out.println("-----------Begin create List-------------");

Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);

       //获取动态代理的构造参数,接收的类型是InvocationHandler

       class MyInvocationHandlerimplements InvocationHandler{

           //因为InvocationHandler为接口类型不可以new也就是实例化出来,

       //所以定义自己的MyInvocationHandler类去是实现InvocationHandler接口,必须覆盖invoke方法。

           @Override

           public Object invoke(Object proxy, Method method, Object[]args)

                  throws Throwable {

              // TODO Auto-generatedmethod stub

              return null;

           }

       }

       Collection proxy1 = (Collection)constructor.newInstance(newMyInvocationHandler());

       System.out.println(proxy1);//打印出null

       proxy1.clear();//打印出null

       proxy1.size();//运行空指针异常,发现returnnull;而size返回的是int

 

---------------------------------------------------------------

//讲前面的两步简化成一步实现,Proxy提供了newProxyInstance

//Foo f = (Foo)Proxy.newProxyInstance(

//Foo.class.getClassLoader(),

//new Class[] { Foo.class },

//handler);

       Collection proxy3 = (Collection)Proxy.newProxyInstance(

       Collection.class.getClassLoader(),//类加载器

       new Class[]{Collection.class},//加载的类,写在大括号,为多个实现类。此处为Collection

       new InvocationHandler(){//实现InvocationHandler接口的子类,匿名内部类方法

       ArrayList target =newArrayList();

       public Object invoke(Object proxy, Method method, Object[]args)

              throws Throwable {

              //ArrayListtarget = new ArrayList();

              long beginTime = System.currentTimeMillis();

              Object retVal = method.invoke(target, args);

              long endTime = System.currentTimeMillis();

              System.out.println(method.getName()+"running time of "+(endTime-beginTime));

              return retVal;

           }});

       proxy3.add("xxx");//每调用一次add方法,就走了invoke方法一次

       proxy3.add("dddd");

       proxy3.add("ffff");

       System.out.println(proxy3.size());//打印出3

System.out.println(proxy3.getClass().getName());//为什么返回的不是ArrayList呢?不是调用了invoke方法了吗?

//通过查询文档了解,getClass()继承与Object,这个上帝只允许hashCodetoStringequals方法访问Handler,其他方法不交给 Handler

       System.out.println(proxy3.toString());//可以验证了以上说法

  1. 猜想分析动态生成的类的内部代码

a)       构造方法接受一个InvocationHandler对象,接受对象了要干什么呢?该方法内部代码会是怎样的?

l        接受是为了创建一个对象记住它,方便以后使用。所以构造函数的代码应该是这样写的

InvocationHandler handler;

Public $Proxy0(InvocationHandler handler){

this. handler = handler ;

}

l          

b)     

c)      客户端(client)调用动态代理$Proxy1的构造方法,构造方法传入InvocationHandler,然后在去找到invoke方法,内部加入日志功能,然后在去找目标的方法。



### Java代理是什么? Java代理是一种设计模式,允许开发者在运行时创建一个代理对象,用来控制对其他对象的访问。这种机制能够拦截对目标对象的方法调用,并在调用前后执行额外的逻辑,例如日志记录、性能监控、权限验证等。Java代理主要分为**静态代理**和**动态代理**两种形式。 静态代理需要在编译时明确指定代理类和目标类的关系,而动态代理则是在运行时通过反射机制动态生成代理类。动态代理的灵活性更高,常用于框架和库的实现中,例如Spring AOP、Hibernate等。 ### Java动态代理的实现方式 Java动态代理主要有两种实现方式:**JDK Proxy** 和 **CGLib**。 1. **JDK Proxy**:这是Java原生支持的一种动态代理方式,它要求目标类必须实现一个接口。JDK Proxy通过`java.lang.reflect.Proxy`类来动态生成代理对象,并使用`java.lang.reflect.InvocationHandler`接口来拦截方法调用[^1]。 - **优点**:无需引入第三方库,仅依赖JDK。 - **缺点**:只能对接口进行代理,不能对类进行代理。 2. **CGLib(Code Generation Library)**:CGLib是一个强大的高性能的代码生成库,它通过继承目标类并生成子类的方式来实现代理功能。因此,CGLib可以对类进行代理,而不仅限于接口[^1]。 - **优点**:支持对类进行代理,适用范围更广。 - **缺点**:需要引入第三方库,生成的代理类可能会增加内存消耗。 ### 如何使用Java动态代理? 以下是一个使用JDK Proxy实现动态代理的简单示例: 1. **定义接口和实现类** ```java // 定义接口 public interface UserService { void addUser(); } // 实现类 public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("Adding user..."); } } ``` 2. **创建动态代理处理器** ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class LoggingHandler implements InvocationHandler { private Object target; public LoggingHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method: " + method.getName()); return result; } } ``` 3. **创建动态代理对象并调用方法** ```java import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); LoggingHandler handler = new LoggingHandler(userService); UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler ); proxy.addUser(); // 代理对象调用方法 } } ``` 4. **运行结果** ``` Before method: addUser Adding user... After method: addUser ``` 在这个示例中,`LoggingHandler`拦截了`addUser`方法的调用,并在方法执行前后添加了日志记录逻辑。这种机制非常适合用于实现横切关注点(如日志、事务管理等)[^2]。 ### 何时使用Java动态代理Java动态代理适用于以下场景: - **日志记录**:在方法调用前后记录日志信息。 - **性能监控**:统计方法执行时间。 - **权限控制**:在方法执行前检查用户权限。 - **事务管理**:在方法执行前后开启或提交事务。 ### 总结 Java代理是一种强大的编程技术,尤其在动态代理的实现中,JDK Proxy和CGLib各有优劣。JDK Proxy适用于接口代理,而CGLib则适用于类代理。根据具体需求选择合适的代理方式,可以显著提升代码的灵活性和可维护性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值