文章目录
前言
阅读本文,你可以了解到java设计模式中的代理模式,再深入到了解java动态代理。
一、代理模式的定义
为什么需要代理模式?:当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。
简单来说:代理模式就是提供一个“代理”的第三方(代购网站),由于顾客无法直接购买海外商品,便通过代理模式提供的第三方间接访问(代购商品)
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问
二、代理模式结构
1.代理模式结构图
2.代理模式简单实现
抽象主题类Subject
:类中声明了真实主题类(RealSubject)和代理类(Proxy)的公共方法,它可以是接口,抽象类或具体类。
public abstract class Subject{
public abstract void request();
}
真实主题类RealSubject
:继承抽象主题类,类中提供业务方法的具体实现。
public class RealSubject extends Subject{
public void request(){
//具体实现代码
}
}
代理类Proxy
:继承抽象主题类,维持一个对真实主题类的对象引用,调用真实主题对象实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法对功能进行扩充或约束。
public class Proxy extends Subject{
private Subject subject=new RealSubject();
public void preRequest(){
//在对象访问前进行的具体操作
}
public void request(){
preRequest();
subject.request();//调用真实对象自己实现的业务方法
postRequest();
};
public void postRequest(){
//在对象访问后进行的具体操作
}
}
客户端代码
:通过代理类间接访问
public class Client{
public static void main(String args[]){
Subject subject=new Proxy();
subject.request();//透明性:对客户端来说是直接操作真实对象
}
}
三、java动态代理
1.动态代理定义
为什么需要动态代理?:
静态代理:每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称为静态代理(Static Proxy)
静态代理缺点:当需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类这将导致系统中类个数急剧增加。
动态代理:在系统运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法
2.java实现动态代理
2.1.java支持动态代理实现所需的类
Java语言提供了对动态代理的支持,Java实现动态代理需要用到java.lang.reflect包中的一些类
- Proxy类
Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类。
类中该方法用于返回一个动态创建的代理类的实例:
public static Obiect newProxyInstance(ClassLoader loader,Clas<?>[] interfaces,InvocationHandler h)
/*
第一个参数Loader:表示代理类的类加载器
第二个参数interfaces:表示代理类所实现的接口列表(与真实主题类的接口列表一致)
第三个参数h:表示所指派的调用处理程序类
*/
- InvocationHandler接口
该接口中声明了以下方法:
public Object invoke (Obiect proxy,Method method,Obiect[] args)
/*
第一个参数proxy:表示代理类的实例
第二个参数method:表示需要代理的方法
第三个参数args:表示代理方法的参数数组
*/
java动态代理的实现与上面静态代理不同之处:
1.静态代理我们需要写一个代理类且代理类所实现的接口以及内部方法的实现均是已经存在的,而用java实现动态代理,我们不需要写代理类(由Proxy.newProxyInstance()提供代理对象)。
2.实现动态代理我们需要写一个处理程序类,该类应实现InvocationHandler接口,并在该类中实现的invoke()方法中处理对所代理的类的实例方法调用并返回相应结果,当一个代理实例中的业务方法被调用时将自动调用该方法!
动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时调用请求会将请求自动转发给InvocationHandler对象的incoke()方法,由invoke()方法来实现对请求的统一处理。
2.2.案例说明
下面用一个案例来说明如何使用动态代理实现对多个真实主题类的统一代理和集中控制
_案例:某软件公司要为公司OA系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现
说明:需代理两个具体主题类:UserDao DocumentDao
需增加操作:1.显示方法被调用时间 2.显示调用结果
2.3.案例实现
抽象主题类1
:抽象用户DAO类
public interface AbstractUserDao {
public boolean findUserById(String UserId);
}
抽象主题类2
:抽象文档DAO类
public interface AbstractDocumentDao {
public boolean delectDocumentById(String DocumentId);
}
真实主题类1
:userDao实现抽象主题类1
public class UserDao implements AbstractUserDao {
@Override
public boolean findUserById(String UserId) {
if(UserId.equals("xxx")){
System.out.println("查询User:"+UserId+"成功");
return true;
}else {
System.out.println("查询User:"+UserId+"失败");
return false;
}
}
}
真实主题类2
:documentDAO实现抽象主题类2
public class DocumentDao implements AbstractDocumentDao {
@Override
public boolean delectDocumentById(String DocumentId) {
if(DocumentId.equals("xxx.txt")){
System.out.println("删除文档:"+DocumentId+"成功");
return true;
}else {
System.out.println("删除文档:"+DocumentId+"失败");
return false;
}
}
}
自定义请求处理程序类DaoLogHandler
:实现InvocationHandler接口并在类中实现对真实主题对象方法的调用及新增业务操作
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class DaoLogHandler implements InvocationHandler {
private Calendar calendar;
private Object object;
public DaoLogHandler(){}
//注入需要提供代理的真实主题对象
public DaoLogHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
Object result = method.invoke(object, args);//转发调用
afterInvoke();
return result;
}
public void beforeInvoke(){
calendar=new GregorianCalendar();
int hour=calendar.get(Calendar.HOUR_OF_DAY);
int min=calendar.get(Calendar.MINUTE);
int second=calendar.get(Calendar.SECOND);
String time=hour+":"+min+":"+second;
System.out.println("调用时间: "+time);
}
public void afterInvoke(){
System.out.println("方法调用结束!");
}
}
客户端Client
:
import Demo.Dao.AbstractDocumentDao;
import Demo.Dao.AbstractUserDao;
import Demo.Dao.DocumentDao;
import Demo.Dao.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
InvocationHandler handler=null;
AbstractUserDao userDao=new UserDao();
handler=new DaoLogHandler(userDao);
AbstractUserDao proxy=null;
//动态创建代理对象,用于代理一个AbstractUserDao类型的真实主题对象
proxy= (AbstractUserDao) Proxy.newProxyInstance(AbstractUserDao.class.getClassLoader(),new Class[]{AbstractUserDao.class},handler);
proxy.findUserById("xxx");//调用代理对象的业务方法
System.out.println("-------------------------------------------------");
AbstractDocumentDao documentDao=new DocumentDao();
handler=new DaoLogHandler(documentDao);
AbstractDocumentDao proxy_new=null;
//动态创建代理对象,用于代理一个AbstractDocumentDao类型的真实主题对象
proxy_new=(AbstractDocumentDao)Proxy.newProxyInstance(AbstractDocumentDao.class.getClassLoader(), new Class[]{AbstractDocumentDao.class}, handler);
proxy_new.delectDocumentById("xxx.txt");
}
}
运行结果
:
调用时间: 20:22:25
查询User:xxx成功
方法调用结束!
-------------------------------------------------
调用时间: 20:22:25
删除文档:xxx.txt成功
方法调用结束!
Process finished with exit code 0
总结
动态代理是一种较为高级的代理模式,它在事务管理,AOP(面向切面编程)等领域都发挥了重要的作用
参考:清华大学出版社 Java设计模式---------------刘伟