代理模式的结构:

● Proxy :代理对象,实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象。可以控制对具体目标对象的访问,并可以负责创建和删除它。
● Subject :目标接口,定义代理和目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
● RealSubject :具体的目标对象,真正实现目标接口要求的功能。
代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象后,对客户端并没有什么影响,就跟得到一个真实对象一样来使用。当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理来操作的,也就是客户端操作代理,代理操作真正的对象。正是因为有代理对象夹在客户端和被代理的真实对象中间,相当于一个中转,那么在中转的时候就可以有许多花招可以玩,比如,判断一下权限,如果没有足够的权限就不给你中转了。
代理的分类:
● 虚代理:根据需要来创建开销很大的对象,该对象只有在需要的时候才真正被创建。
● 远程代理:用来在不同的地址空间上代表一个对象,这个不同的地址空间可以是本机,也可以在其他机器上。在Java里最典型的就是RMI
● copy-on-write 代理:在客户端操作的时候,只有对象却是改变了,才会真正的copy(或clone)一个对象,算是虚代理的一个分支。
● 保护代理:控制对原始对象的访问,如果需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问。
● Cache 代理:为那些昂贵操作的结果提供临时的存储空间,以便多个客户可以共享这些结果。
● 防火墙代理:保护对象不被恶意用户访问和操作。
● 同步代理:使多个用户能够同时访问目标对象而没有冲突。
● 智能代理:在访问对象的时候执行一些附加操作,比如,对指向实际对象的引用计数、第一次引用一个持久对象时,将他装入内存等。
㈠ 虚代理
可以根据需要来创建“大”对象,只要到必须创建的时候,虚代理才会创建对象,从而大大加快程序运行的速度,并节省资源。通过虚代理可以对系统进行优化。
/**
* 定义用户数据对象的接口
*
*/
public interface UserModelApi {
public String getUserId();
public void setUserId(String userId);
public String getName();
public void setName(String name);
public String getDepId();
public voidsetDepId(String depId);
public String getSex();
public void setSex(String sex);
}
/**
* 目标对象
*
*/
public class UserModelimplements UserModelApi {
private String userId;
private String name;
private String depId;
private String sex;
//省略setter and getter
}
/**
* 代理对象,代理用户数据对象
*
*/
public class Proxy implements UserModelApi {
private final UserModeluserModel;
/**
* 标识是否已经重新装载过数据
*/
private boolean loaded= false;
public Proxy(UserModel userModel){
this.userModel= userModel;
}
private void reload(){
System.out.println("重新查询数据库获取完整的数据,userId=="+userModel.getUserId());
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from student where userId = ?";
try{
conn = Database.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userModel.getUserId());
rs = pstmt.executeQuery();
if(rs.next()){
userModel.setDepId(rs.getString("depId"));
userModel.setSex(rs.getString("sex"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
Database.close(conn, pstmt, rs);
}
}
@Override
public String getUserId() {
return userModel.getUserId();
}
@Override
public void setUserId(String userId) {
userModel.setUserId(userId);
}
@Override
public String getName() {
return userModel.getName();
}
@Override
public void setName(String name) {
userModel.setName(name);
}
@Override
public String getDepId() {
if(!this.loaded){
reload();
this.loaded= true;
}
return userModel.getDepId();
}
@Override
public void setDepId(String depId) {
userModel.setDepId(depId);
}
@Override
public String getSex() {
if(!this.loaded){
reload();
this.loaded =true;
}
return userModel.getSex();
}
@Override
public void setSex(String sex) {
userModel.setSex(sex);
}
}
public class UserManager {
/**
* 根据部门编号来获取所有人
* @param depId, 部门编号
* @return 该部门下的所有人
*/
public Collection<UserModelApi> getUserByDepId(String depId){
Collection<UserModelApi> col = newArrayList<UserModelApi>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select userId, name from student where depId like ?";
try{
conn = Database.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, depId + "%");
rs = pstmt.executeQuery();
while(rs.next()){
Proxy proxy = newProxy(new UserModel());
proxy.setUserId(rs.getString("userId"));
proxy.setName(rs.getString("name"));
col.add(proxy);
}
}catch(SQLException e){
e.printStackTrace();
}finally{
Database.close(conn, pstmt, rs);
}
return col;
}
}
public class Client {
public static void main(String[] args) {
UserManager manager = newUserManager();
Collection<UserModelApi> col = manager.getUserByDepId("0101");
//如果只显示用户名称,则不需要重新查询数据库
for(UserModelApi user : col){
System.out.println("用户编号:=" + user.getUserId() + ",用户姓名:=" + user.getName());
}
//如果访问非用户编号和用户名以外的属性,那就会重新查询数据库
for(UserModelApi user : col){
System.out.println("用户编号:=" + user.getUserId() + ",用户姓名:=" + user.getName() +
",所属部门:=" + user.getDepId());
}
}
}
/**
* 数据库对象,存放一些操作数据库的方法
*
*/
public class Database {
private static final StringDATABASE_CONFIG_FILE = "databaseConfigFile";
private static StringDRIVER;
private static StringURL;
private static StringUSERNAME;
private static StringPASSWORD;
static{
ResourceBundle resource = ResourceBundle.getBundle(DATABASE_CONFIG_FILE);
DRIVER = resource.getString("DRIVER").trim();
URL = resource.getString("URL").trim();
USERNAME = resource.getString("USERNAME").trim();
PASSWORD = resource.getString("PASSWORD").trim();
}
public static Connection getConnection()throws SQLException{
Connection conn = null;
try{
Class.forName(DRIVER);
conn = DriverManager.getConnection(URL,USERNAME , PASSWORD);
}catch(ClassNotFoundException e){
e.printStackTrace();
}
return conn;
}
public static void closeConnection(Connection conn){
try{
if(conn !=null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
public static void closePreparedStatement(PreparedStatement pstmt){
try{
if(pstmt !=null){
pstmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
public static void closeResultSet(ResultSet rs){
try{
if(rs !=null){
rs.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs){
closeResultSet(rs);
closePreparedStatement(pstmt);
closeConnection(conn);
}
}
/* 数据库配置文件 */
src/databaseConfigFile.properties
DRIVER=com.microsoft.jdbc.sqlserver.SQLServerDriver
URL=jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=demo
USERNAME=sa
PASSWORD=joe
从上面的实例可以看出,如果只访问用户编号和用户姓名的数据,是不需要重新查询数据库的。只有当访问到这两个数据以外的数据时,才需要重新查询数据库获得完整的数据,从而节省了内存。但这有个潜在的问题,就是当要查询全部的数据时,就会执行N+1 次数据查询。这类似Hibernate 中的 Lazy Load 的情况也存在 N + 1次查询的操作,原因就在于,Hibernate 的 Lazy Load 就是使用代理来实现的。
原文:http://joe5456536.blog.163.com/blog/static/853747732011762349570/