一:动态代理和静态代理的区别
静态代理:了解设计模式中的代理模式的童鞋应该都知道,如果想要生成代理类,需要让代理类和被代理类实现同一个接口,并且在代理类中添加被代理类的引用,代理类方法实现中添加代理逻辑,并决定是否调用被代理类方法,这种通过硬编码的方式指定代理类与被代理类的方式,叫静态代理。可以明显看出,静态代理类与被代理类是强耦合的,如果要代理100个类,你就得写100个代理类
动态代理:其实动态代理与静态代理的本质一样,最终程序运行时都需要生成一个代理对象实例,通过它来完成相关增强以及业务逻辑,只不过静态代理需要硬编码的方式指定,而动态代理则是以动态方式生成代理(有编译时操作字节码生成的方式、运行时通过反射、字节码生成的方式)。动态生成的好处很明显,代理逻辑与业务逻辑是互相独立的,没有耦合,代理1个类或100个类要做的事情没有任何区别
说到耦合,必须把AOP拿来说道说道:传统面向对象思想中,如果想要实现功能复用,要么继承、要么引用,无论哪种方式,对代码都有一定的侵入性,耦合无可避免,侵入性啥意思?简单来说:如果你想要用它增强你程序的功能,你必须改动你的程序代码,那它就具有侵入性。如果只有一点两点需要增强还好说,如果大量的功能点需要被增强,工作量就会很大,代码也不太优雅。想象一下,如果你对外公开了一系列的接口,现在领导说了,接口要加权限控制。在哪加?最笨的当然就是写个程序验证的逻辑,然后每个接口都拿来调用一遍。这也正是面向对象思想的短板,在要为程序新增一些通用功能时,只能通过耦合的方式才能进行。AOP正是为此而生,AOP旨在通过一种无耦合的方式来为程序带来增强。而动态代理,就是AOP实现方式中的一种
二:动态代理相关的类
Proxy:生成动态代理的实例
InvocationHandler:
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。(invoke()方法可以通过反射加载被代理的类的方法)
三:代码实现
1:接口
package com.wyj.demo03;public interface UserService {void deleteUser();void addUser();void updateUser();void selectUser();
}
2:实现类
package com.wyj.demo03;import com.wyj.demo03.UserService;public class UserServiceImp implements UserService {@Overridepublic void deleteUser() {System.out.println("删除一个用户");}@Overridepublic void addUser() {System.out.println("增加一个用户");}@Overridepublic void updateUser() {System.out.println("修改一个用户");}@Overridepublic void selectUser() {System.out.println("查询一个用户");}
}
3:生成动态代理类的程序
package com.wyj.demo03;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//生成代理类的程序
public class ProxyInvocationHandler implements InvocationHandler {//被代理的类private Object target;//设置要代理的类(这个就是我们具体要代理的类)public void setTarget(Object target) {this.target = target;}//生成得到代理类(这里我们返回的是一个接口 也就是被代理的类 所实现的接口)public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}/**** @param proxy :被代理的对象* @param method:被代理的对象的方法* @param args :被代理的对象的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {log(method.getName());Object invoke = method.invoke(target, args);//在反射中通过 方法名.invoke(类名);传统方法中 类名.方法名();return invoke;}//增加需求 打印日志public void log(String msg) {System.out.println("调用了"+msg);}
}
其中的参数解析
类加载机制:
首先我们要了解一下类的加载机制,在每创建一个Java类时,都会生产一个.class文件,在内存中对应也会生成一个class对象,来表示该类的类型信息,我们可以用.class来获取这个类的所有信息,也可以通过getClass()方法来读取这个类的所有信息,比如getClass().getInterfaces()获取类的接口信息等。在Java类加载时,要通过一个类加载器classloader来将生成的Java类加载到JVM中才能执行。理解了类的加载机制后,我们再看代码中的newProxyInstance方法,在这个方法中,我们将被代理对象传进来后.
在Proxy.newProxyInstance方法中,共有三个参数:
1、target.getClass().getClassLoader()目标对象通过getClass方法获取类的所有信息后,调用getClassLoader()方法来获取类加载器。获取类加载器后,可以通过这个类型的加载器,在程序运行时,将生成的代理类加载到JVM即Java虚拟机中,以便运行时需要!
2、target.getClass().getInterfaces()获取被代理类的所有接口信息,以便于生成的代理类可以具有代理类接口中的所有方法。
3、this:我们使用动态代理是为了更好的扩展,比如在方法之前做什么,之后做什么等操作。这个时候这些公共的操作可以统一交给代理类去做。这个时候需要调用实现了InvocationHandler 类的一个回调方法。由于自身实现了这个方法,所以将this传递过去。
(也就是在这个ProxyInvocationHandler 类自身中实现一些需求的方法)
4:实现过程
package com.wyj.demo03;public class Client {public static void main(String[] args) {//真实角色UserServiceImp serviceImp = new UserServiceImp();//代理角色不存在ProxyInvocationHandler handler = new ProxyInvocationHandler();//设置被代理的对象handler.setTarget(serviceImp);//得到代理类对象(这个是我们返回是一个接口,我们的被代理类是实现的一个接口)UserService proxy = (UserService) handler.getProxy();proxy.deleteUser();}
}
实现过程的解析:
代理类是在运行时创建的实现指定的接口列表(称为代理接口)的类 。
代理实例(proxy)是代理类的一个实例。 每个代理实例(proxy)都有一个关联的调用处理程序对象(handler),它实现接口InvocationHandler 。
通过其代理接口之一的代理实例上的方法,调用将被分派到实例调用处理程序的invoke方法,传递代理实例,标识调用方法的java.lang.reflect.Method对象以及包含参数的类型为Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。