JDK动态代理
代理设计模式属于java中21种设计模式的一种,属于结构型模式
代理模式主要用途是对目标对象进行功能的扩展(Spring中的AOP就是用代理模式实现的)
代理模式分为三个角色:抽象角色、目标角色、代理角色
- 目标角色:指的是用于处理请求的业务逻辑代码对象
- 代理角色:指的是对目标角色进行功能的扩展,来执行一些非业务逻辑的代码
- 抽象角色:定义一些需要完成业务逻辑的行为操作(jdk动态代理是一个接口)
角色之间的关系:
- 目标角色和代理角色都属于抽象角色的子类
- 目标角色和代理角色完成的行为操作是一致的
- 代理角色会持有一个目标角色对象的引用
根据代理方式不同,分为静态代理和动态代理
静态代理:
静态代理只能代理一种类型的对象,如果存在多种抽象角色,需要实现多个代理类
代理的方法比较多时,会出现大量的重复性代码,不利于维护和管理
动态代理:
JDK动态代理解决所有的静态代理的问题
只需要提供一个扩展类(InvocationHandler)来扩展代码,扩展代码只需要编写一次
JDK动态代理只能通过接口才能代理(抽象角色必须是接口)
下面有一个实例,主要是对已有的代码进行扩展,在方法的前后加上时间日志(或其他,根据业务需求),一般情况业务代码都在service层,我就直接加到service层中,便于大家在项目中对照
1.抽象角色(就相当于service层中的一个接口)
package com.yubo.proxy;
/**
* 抽象角色
* @author Administrator
*
*/
public interface TestService {
void delete(Integer id);
void save(String username,Integer age);
String findUserList(String username);
}
2.目标角色(需要实现抽象接口)
package com.yubo.proxy;
/**
* 目标角色
* @author Administrator
*
*/
public class TestServiceImpl implements TestService {
@Override
public void delete(Integer id) {
try {
Thread.sleep(2000);
System.out.println("delete-->" + id);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void save(String username, Integer age) {
System.out.println("save-->"+username+"-"+age);
}
@Override
public String findUserList(String username) {
System.out.println("findUserList-->"+username);
return username;
}
}
3.扩展类(需要实现InvocationHandler接口)
package com.yubo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 扩展类
* @author Administrator
*
*/
public class TimeHandler implements InvocationHandler {
//目标对象
private Object testServiceImpl;
//有参数的构造方法
public TimeHandler(Object testServiceImpl){
this.testServiceImpl = testServiceImpl;
}
/**
* 第一个参数(Object proxy):代理对象
* 第二个参数(Method method):目标对象上的方法
* 第三个参数(Object[] args):目标对象上方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("方法开始时间为:"+new Date());
//目标方法运行
Object result = method.invoke(testServiceImpl, args);
System.out.println("方法结束时间为:"+new Date());
return result;
}
}
4.测试类
package com.yubo.proxy;
import java.lang.reflect.Proxy;
public class MainTest {
public static void main(String[] args) {
//目标对象
TestService test = new TestServiceImpl();
// test.delete(200);
//扩展对象
TimeHandler handler = new TimeHandler(test);
//代理对象:动态代理的代理类是在内存中动态产生的
//代理类默认的名称:$Proxy0
Object proxy = Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), handler);
TestService proxyTest = (TestService) proxy;
proxyTest.delete(200);
}
}
几点需要注意:
- 测试类中Proxy.newProxyInstance(test.getClass().getClassLoader(), test.getClass().getInterfaces(), handler);这个方法,参数说明如下:第一个参数:表示类的加载器,动态代理的代理类是动态产生的,需要运行也得通过类的加载器进行加载,直接使用目标对象的类加载器就可以;第二个参数:表示代理类和目标类共同的抽象角色,所以传递目标对象所实现的接口类型;第三个参数:表示目标对象进行扩展的对象,当调用代理对象时,代理对象会调用扩展对象,由扩展对象进行功能扩展,然后执行目标对象的业务逻辑代码
- 代理类和目标类实现的是共同的接口,所以将代理类转换成接口类型是可以的
- 生成的代理对象的通常会是如下类似代码,了解一下,便于理解
//伪代码
public class $proxy0 implements TestService {
private InvocationHandler h;
public void save(String username, Integer age) {
h.invoke(this, method, args[]);
}
public void delete(Integer id) {
h.invoke(this, method, args[]);
}
public String findUserList(String username) {
return h.invoke(this, method, args[]);
}
}
至此jdk的动态代理就全部编写完毕,最重要的用途就是在不动原来代码的基础上增加一些新的需求或者功能,也符合ocp的原则。
有什么问题欢迎留言,写博客也是一种学习的记录,我会坚持。