为其他对象提供了一种代理以控制对这个对象的访问.
代理对象在客户端和目标对象之间起到中介作用,属于结构性模式。
目的
- 保护对象
- 增强目标对象
- Subject是顶层接口;
- RealSubject是真实对象(被代理对象)
- Proxy是的代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理逻辑.
一般分为静态代理和动态代理两种方式.
代理模式的优点
职责清晰: 真实角色就是实现实际的业务逻辑,不用关心其他非本职的事物。
高扩展性: 真实角色可以随时更换或扩展,只需要实现接口就行,而代理不需要有任何变化
静态代理
举个例子,在古代相亲时,父亲都请来媒婆为儿子物色对象,媒婆即做了儿子的代理角色
- Subject是顶层接口,Person
public interface Person {
public void findLove();
}
- RealSubject是真实对象Son,实现了顶层接口
public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要求:肤白貌美大长腿");
}
}
- Proxy代理对象,实现Subject顶层接口,并持有RealSubject真实对象
public class SteadyProxy {
public static void main(String[] args) {
// 媒婆帮儿子找对象
SteadyMeipo meipo = new SteadyMeipo(new Son());
meipo.findLove();
}
}
动态代理
分为jdk和cglib两种方式:jdk方式使用反射技术,cglib底层使用asm技术
jdk方式
两个重要类:
-
interface InvocationHandler
Object invoke(Object proxy, Method method, Object[] args)
动态生成代理类,调用被代理类的方法,完成其他方法的织入 -
class Proxy
真正生成动态代理的类,提供两个静态方法- Class<?> getProxyClass(ClassLoader loader, Class<?>[] interface)
用来产生代理类,参数要提供interface数组,它会生成这些interface的“虚拟实现”,
用来冒充真实的对象。 - Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
产生代理对象,多了InvocationHandler参数(只是InvocationHandler接口的实现类),
它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(…)方法
- Class<?> getProxyClass(ClassLoader loader, Class<?>[] interface)
案例
- 动态代理类生成类,实现InvocationHandler接口
public class JDKMeipo implements InvocationHandler {
// 被代理的对象,把引用保存下来
private Object target;
public Object getInstance(Object object){
this.target = object;
Class<?> clazz = object.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader() , clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object object = method.invoke(this.target , args);
after();
return object;
}
private void before(){
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after(){
System.out.println("如果合适的话,就准备办事");
}
}
- client代理测试
public class Test {
public static void main(String[] args) {
Person obj = (Person) new JDKMeipo().getInstance(new Customer());
obj.findLove();
}
}
debug发现此时生成的Person对象为$Proxy0@1234,其实就是为我们动态生成了代理类,执行时使用该代理类进行程序执行。
可以使用如下代码将生成的代理类反编译出来,查看真正的代理类代码:
public class Test {
public static void main(String[] args) {
Person obj = (Person) new JDKMeipo().getInstance(new Customer());
obj.findLove();
// 动态代理生成的类使用字节码重组,这里将其保存在硬盘上,后反编译分析
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream os = null;
try {
os = new FileOutputStream("D://$Proxy0.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行上面的代码,会在D盘生成一个$Proxy0.class文件,使用相应的反编译工具,即可看到其源文件,如下所示为:
public final class Proxy0 extends Proxy implements Person{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public Proxy0(){
super(paramInvocationHandler);
}
public final boolean equals(){
try{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException){
throw localRuntimeException;
}
catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void findLove(){
try{
this.h.invoke(this, m3, null);
return;
}catch (RuntimeException localRuntimeException){
throw localRuntimeException;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() {
try{
return ((String)this.h.invoke(this, m2, null));
}catch (RuntimeException localRuntimeException){
throw localRuntimeException;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode(){
try{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}catch (RuntimeException localRuntimeException){
throw localRuntimeException;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
static{
try{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.melody.proxy.manul.dynamic.Person").getMethod("findLove", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}catch (NoSuchMethodException localNoSuchMethodException){
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException){
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
观察发现:$Proxy0继承了Proxy类,同时实现了Person接口,而且重写了Person中的findLove方法,在静态快中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。
手写动态代理类的产生逻辑
- 创建GPInvocationHandler接口,对应jdk中的InvocationHandler类
public interface GPInvovationHandler {
public Object invoke(Object proxy, Method method, Object... args) throws Throwable;
}
- 创建GPProxy类,对应jdk中的Proxy类,主要负责生产代理类
public class GPProxy {
public static final String ln = "\r\n";
public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces,
GPInvovationHandler handler) {
// 动态生成源代码.java文件
String src = generateSrc(interfaces);
System.out.println(src);
try {
// java文件输出磁盘
String filePath = GPProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
FileWriter fw;
fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
// 把生成的java文件编译成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manager.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//把生成的class文件加载到JVM中
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(GPInvovationHandler.class);
f.delete();
return c.newInstance(handler);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSrc(Class<?>[] interfaces) {
StringBuilder sb = new StringBuilder();
sb.append("package com.melody.proxy.dynamic.manual;" + ln);
sb.append("import com.melody.proxy.dynamic.manual.Person;" + ln);
sb.append("import java.lang.reflect.*;" + ln);
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
sb.append("GPInvovationHandler h;" + ln);
sb.append("public $Proxy0(GPInvovationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);
for (Method m : interfaces[0].getMethods()) {
Class<?>[] params = m.getParameterTypes();
StringBuilder paramNames = new StringBuilder();
StringBuilder paramValues = new StringBuilder();
StringBuilder paramClasses = new StringBuilder();
for (int i = 0; i < params.length; i++) {
Class clazz = params[i];
String type = clazz.getName();
String paramName = toLowerFirstCase(clazz.getSimpleName());
paramNames.append(type + " " + paramName);
paramValues.append(paramName);
paramClasses.append(clazz.getName() + ".class");
if (i > 0 && i < params.length - 1) {
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ")" +
" " +
"{" + ln);
sb.append("try{" + ln);
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new " +
"Class[]{" + paramClasses.toString() + "});" + ln);
sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new " +
"Object[]{" + paramValues + "})", m.getReturnType()) + ";" + ln);
sb.append("} catch (NoSuchMethodException e) {\n" +
"\t\t\te.printStackTrace();\n" +
"\t\t} catch (Throwable throwable) {\n" +
"\t\t\tthrowable.printStackTrace();\n" +
"\t\t}");
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
return sb.toString();
}
private static Map<Class, Class> mappings = new HashMap<>();
static {
mappings.put(int.class, Integer.class);
}
private static String getReturnEmptyCode(Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
return "return 0;";
} else if (returnClass == void.class) {
return "";
} else {
return null;
}
}
private static String getCaseCode(String code, Class<?> returnClass) {
if (mappings.containsKey(returnClass)) {
return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() +
"Value()";
}
return code;
}
private static boolean hasReturnValue(Class<?> clazz) {
return clazz != void.class;
}
private static String toLowerFirstCase(String src) {
char[] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
- 创建GPClassLoader。类加载器,负责将上述生产的$Proxy0代理类加载到jvm中
public class GPClassLoader extends ClassLoader {
private File classPathFile;
public GPClassLoader() {
String classPath = GPClassLoader.class.getResource("").getPath();
classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) {
String className = GPClassLoader.class.getPackage().getName() + "." + name;
if (classPathFile != null) {
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
- 创建GPMeipo媒婆来,调用GPProxy进行对代理对象的功能进行增强
public class GPMeipo implements GPInvovationHandler {
/** 被代理的对象,把引用保存下来 */
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
return GPProxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(this.target, args);
after();
return null;
}
private void before() {
System.out.println("我是媒婆,我要给你找对偶像,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after() {
System.out.println("如果合适的话,就主备办事");
}
}
- 测试类如下:
public class Main {
public static void main(String[] args) {
Person p = (Person) new GPMeipo().getInstance(new Customer());
System.out.println(p.getClass());
p.findLove();
}
}
到此,手写jdk动态代理就完成了
cglib方式
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
- cglib实现的媒婆实现类CglibMeipo如下:对应上述的GPMeipo 类
public class CglibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception {
Enhancer enhancer = new Enhancer();
//要把哪个设置为即将生成的新类的父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 业务的增强
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("我是媒婆:我要给你介绍对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after() {
System.out.println("如果合适的话,就主备办事");
}
}
- 测试类如下:
public class CglibTest {
public static void main(String[] args) {
try {
// 将生成的代理类写入如下文件夹
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY , "D://cglib");
Person obj = (Person) new CglibMeipo().getInstance(Son.class);
obj.findLove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
cglib代理执行效率之所以比jdk高,是因为cglib采用了fastclass机制,它的简单原理是:为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配一个index,这个index当做一个入参,FastClass就可以直接定位要调用的方法并直接进行调用,省去了反射调用。
具体cglib的原理这里不做详细说明,这里简单看下jdk与cglib实现动态代理的区别
- jdk动态代理实现了被代理对象的接口,cglib代理继承了被代理对象
- jdk动态代理和cglib代理都在运行期生成字节码,jdk动态码直接写Class字节码,cglib代理使用ASM框架写Class字节码,cglib代理实现更复杂,生成代理类比jdk动态代理效率低;
3.jdk动态代理调用代理方法是通过反射机制,cglib代理是通过FastClass机制直接调用方法的,cglib代理的执行效率更高。
AOP中的动态代理模型
面向切面编程,不做过多赘述。
- 抽象主题类
public interface ISubject {
void doSomething(String str);
}
- 真实主题
public class RealSubject implements ISubject {
@Override
public void doSomething(String str) {
System.out.println("做某些逻辑处理-----------");
}
}
- 通知接口和实现
public interface Advice {
public void exec();
}
public class BeforeAdvice implements Advice {
@Override
public void exec() {
System.out.println("前置通知被执行...");
}
}
public class AfterAdvice implements Advice {
@Override
public void exec() {
System.out.println("后置通知被执行...");
}
}
- 动态代理的处理类
public class MyInvocationHandler implements InvocationHandler {
/** 被代理的对象 */
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否符合织入条件
if (true) {
new BeforeAdvice().exec();
}
Object o = method.invoke(this.target, args);
//判断是否符合织入条件
if (true) {
new AfterAdvice().exec();
}
return o;
}
}
- 动态代理类生成类
public class DynamicProxy {
protected static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler invocationHandler) {
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
//具体业务的动态代理生成类,接收ISubject类型,并为其生成相应的代理类
public class SubjectDynamicProxy extends DynamicProxy{
public static ISubject newProxyInstance(ISubject subject) {
// 获得classloader
ClassLoader classLoader = subject.getClass().getClassLoader();
//获得接口数字
Class<?>[] interfaces = subject.getClass().getInterfaces();
// 获得handler
InvocationHandler handler = new MyInvocationHandler(subject);
return (ISubject) newProxyInstance(classLoader, interfaces, handler);
}
}
- 测试类
public class TestClient {
public static void main(String[] args) {
//定义一个主题
ISubject subject = new RealSubject();
//定义subject的代理
ISubject proxy = SubjectDynamicProxy.newProxyInstance(subject);
proxy.doSomething("hahahaha");
}
}
在执行真正的业务方法之前,插入了一个前置通知方法,永远会在业务方法之前发送一个通知,这就是横切面编程:在不改变我们已有代码结构的情况下增强或控制对象的行为。 这里需要注意一点:实现JDK的动态代理,被代理类必须实现一个接口(其他的动态代理技术如CGLIB可以没有接口)