Java代理
代理模式(Proxy)是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能,如添加权限,访问控制和审计等功能。
Java代理分为静态代理和动态代理和Cglib代理,下面进行逐个说明。
静态代理
接口类AdminService.java接口
package com.lance.proxy.demo.service;
public interface AdminService {
void update();
Object find();
}
实现类AdminServiceImpl.java
package com.lance.proxy.demo.service;
public class AdminServiceImpl implements AdminService{
public void update() {
System.out.println("修改管理系统数据");
}
public Object find() {
System.out.println("查看管理系统数据");
return new Object();
}
}
代理类AdminServiceProxy.java
package com.lance.proxy.demo.service;
public class AdminServiceProxy implements AdminService {
private AdminService adminService;
public AdminServiceProxy(AdminService adminService) {
this.adminService = adminService;
}
public void update() {
System.out.println("判断用户是否有权限进行update操作");
adminService.update();
System.out.println("记录用户执行update操作的用户信息、更改内容和时间等");
}
public Object find() {
System.out.println("判断用户是否有权限进行find操作");
System.out.println("记录用户执行find操作的用户信息、查看内容和时间等");
return adminService.find();
}
}
测试类StaticProxyTest.java
package com.lance.proxy.demo.service;
public class StaticProxyTest {
public static void main(String[] args) {
AdminService adminService = new AdminServiceImpl();
AdminServiceProxy proxy = new AdminServiceProxy(adminService);
proxy.update();
System.out.println("=============================");
proxy.find();
}
}
输出:
判断用户是否有权限进行update操作
修改管理系统数据
记录用户执行update操作的用户信息、更改内容和时间等
=============================
判断用户是否有权限进行find操作
记录用户执行find操作的用户信息、查看内容和时间等
查看管理系统数据
总结:
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。
不足:静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。
动态代理
为解决静态代理对象必须实现接口的所有方法的问题,Java给出了动态代理,动态代理具有如下特点:
- Proxy对象不需要implements接口;
- Proxy对象的生成利用JDK的Api,在JVM内存中动态的构建Proxy对象。需要使用java.lang.reflect.Proxy类的
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
*/
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler );
方法,方法参数说明:
- a.ClassLoader loader:指定当前target对象使用类加载器,获取加载器的方法是固定的;
- b.Class<?>[] interfaces:target对象实现的接口的类型,使用泛型方式确认类型
- c.InvocationHandler invocationHandler:事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。
Cglib代理
DK动态代理要求target对象是一个接口的实现对象,假如target对象只是一个单独的对象,并没有实现任何接口,这时候就会用到Cglib代理(Code Generation Library),即通过构建一个子类对象,从而实现对target对象的代理,因此目标对象不能是final类(报错),且目标对象的方法不能是final或static(不执行代理功能)。
Cglib依赖的jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
理解上述Java代理后,也就明白Spring AOP的代理实现模式,即加入Spring中的target是接口的实现时,就使用JDK动态代理,否是就使用Cglib代理。Spring也可以通过<aop:config proxy-target-class=“true”>强制使用Cglib代理,使用Java字节码编辑类库ASM操作字节码来实现,直接以二进制形式动态地生成 stub 类或其他代理类,性能比JDK更强。