JAVA代理模式
Java代理分为静态代理和动态代理。动态代理分为JDK动态代理和cglib动态代理。单纯从各种性能测试来说,JDK优于cglib。强烈建议掌握代理模式,面试高频问题,面向切面编程(AOP)的理念用的就是他。
静态代理
定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类
代码示例:
创建User接口:
public interface User {
void show();
}
创建User接口实现类UserImpl.java
public class UserImpl implements User {
@Override
public void show() {
System.out.println("实现Java静态代理");
}
}
创建代理类Proxy.java
/**
* 静态代理模式
* 必须创建代理类
* 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
*
*/
public class Proxy implements User {
private User user;
//构造函数
public Proxy(final User user){
this.user=user;
}
@Override
public void show() {
System.out.println("插入代码执行前的逻辑代码");
user.show();
System.out.println("插入代码执行后的逻辑代码");
}
}
创建测试类
public class Test {
public static void main(String[] args) {
User user = new UserImpl();
Proxy proxy = new Proxy(user);
proxy.show();
}
}
运行结果:
JDK动态代理
JDK动态代理是利用反射机制在运行时创建代理类。核心是InvocationHandler。每一个代理的实例都会有一个关联的调用处理程序(InvocationHandler)。对待代理实例进行调用时,将对方法的调用进行编码并指派到它的调用处理器(InvocationHandler)的invoke方法。所以对代理对象实例方法的调用都是通过InvocationHandler中的invoke方法来完成的,而invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。
创建User接口
public interface User {
void show();
}
创建User接口实现类UserImpl.java
public class UserImpl implements User{
@Override
public void show() {
System.out.println("实现JDK动态代理");
}
}
创建代理类MyProxyHandler.java
Proxy.newProxyInstance()方法接受三个参数:
- ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
- Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
- InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyProxyHandler implements InvocationHandler {
private Object object;
public MyProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("插入代码执行前的逻辑代码");
Object result = method.invoke(object, args);
System.out.println("插入代码执行后的逻辑代码");
return result;
}
}
创建测试类
public class Test {
public static void main(String[] args) {
User user = new UserImpl();
User o1 = (User) Proxy.newProxyInstance(user.getClass().getClassLoader(),
new Class[]{User.class}, new MyProxyHandler(user));
o1.show();
}
}
运行结果:
cglib动态代理
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
pom.xml文件导入Maven依赖
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
创建User类
public class User {
public void show(){
//实际开发中这里为逻辑业务开发代码
System.out.println("实现cglib动态代理");
}
}
创建代理类MyProxy.java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("可以插入代码执行前的逻辑代码");
Object o1 = methodProxy.invokeSuper(o,objects);
System.out.println("可以插入代码执行后的逻辑代码");
return o1;
}
}
创建测试类
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程
enhancer.setSuperclass(User.class); // 设置enhancer对象的父类
enhancer.setCallback(new MyProxy()); // 设置enhancer的回调对象
User user = (User) enhancer.create(); // 创建代理对象
user.show(); // 通过代理对象调用目标方法
}
}
运行结果:
总结
静态代理总结:
(1)其实,所谓的静态代理,其实就是创建一个类【名为代理类】,然后该类实现了与目标类【业务层对象】一样的接口后,
重写接口内容,里面做增强业务,也就是自定义操作,然后需要执行目标类的方法时,调用注入的目标类[即业务层对象]的方法即可,
说白了就是目标类外再套一层方法而已。
(2)重写目的是保证与目标类的方法一样,不仅可以确保不写错,还可以不用手写,这就很舒服了,
但是并不是说目标类必须要有接口才可以使用静态代理,因此,目标类不论是否有接口都可以使用静态代理。
(3)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。
JDK动态代理总结:
(1)动态代理解决静态代理的缺点,代理类不需要再与目标对象实现一样的接口。
(2)实例代理类后,注入目标类对象即可,然后调用代理类的方法会在内存动态的创建代理对象【使用Proxy.newProxyInstance】后将该代理对象返回,然后强转成目标对象一样的类型即可按照目标类型方法调用,创建代理对象需要输入的参数 分别是 目标类的类加载器 、目标对象实现的接口的类型、事件处理器 ,然后重写事件处理器里的方法,该方法内写自定义的增强业务,而目标对象的方法调用则固定使用 Object returnValue = method.invoke(target, args)完成 。
(3)虽然代理类没有直接实现在目标类所实现的接口,但是在内存动态的创建代理对象 方法的注入参数需要 目标对象实现的接口的类型 ,因此目标类必须要最少实现一个接口 ,否则无法使用动态代理。
(4)缺点:有些类不是业务层的,仅仅是一个单独的类 ,没有实现任何接口,是不能使用动态代理的。
CGLib代理总结:
(1)记得需要导入 cglib依赖包 ,这其实是个代码生成包,在内存中构建一个子类对象从而实现对目标对象功能的扩展。
(2)代理类需要实现MethodInterceptor接口 ,用于重写拦截方法【intercept()】,内部的增强操作方式 与 JDK动态代理十分相似。
(3)创建代理对象需要使用工具类Enhancer。
(4)允许代理那些那么没有实现任何接口的目标类,解决了动态代理的缺点。
(5)底层是ASM字节码框架,不建议直接操作ASM。