为其他对象提供一个代理控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理类和委托类要实现相同的接口,因为代理真正实现的还是委托类的方法。
使用场景:
如果需要委托类处理某一业务,就可以在代理类中统一处理然后调用具体的实现类。
主题接口:
/*
代理模式---------主题接口
*/
public interface Usermanager {
public final String userId=null, userName=null;
public void addUser(String userId, String userName);
public void deluser(String userId);
public String findUser (String userId);
public void modifyUser(String userId, String userName);
}
/*
* 具体用户管理实现类
*/
public class UserManagerImpl implements Usermanager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.adduser");
}
@Override
public void deluser(String userId) {
System.out.println("UserManagerImpl.deluser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.finduser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
/*
* 代理类------用户管理实现类
*/
public class UserManagerImplProxy implements Usermanager {
// 目标对象
private Usermanager um;
// 通过构造方法传入对象
public UserManagerImplProxy(Usermanager um) {
this.um = um;
}
@Override
public void addUser(String userId, String userName) {
try {
// 添加日志打印功能
// 开始添加用户
System.out.println("start----------->adduser()");
um.addUser(userId, userName);
// 添加用户成功
System.out.println("success--------------->adduser()");
} catch (Exception e) {
// 添加用户失败
System.out.println("error------>adduser()");
}
}
@Override
public void deluser(String userId) {
um.deluser(userId);
}
@Override
public String findUser(String userId) {
um.findUser(userId);
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
um.modifyUser(userId, userName);
}
/*
* 测试类
*/
public class Test {
public static void main(String[] args) {
Usermanager um = new UserManagerImplProxy(new UserManagerImpl());
um.addUser("04131146", "张三丰");
}
}
}
运行结果:
start----------->adduser()
UserManagerImpl.adduser
success--------------->adduser()
优点:
编译期加入,提前指定谁调用谁,效率高。
缺点:
1:静态代理很麻烦,需要大量的代理类。(当我们有多个目标对象需要代理时,就需要建立多个代理类)
2:在编译期加入,系统的灵活性低。
应用: 代理类可以对实现类进行统一的管理,如在实现具体之前,需要打印日志信息,这时只需添加一个静态代理,在代理类中添加打印功能,然后调用具体的实现类,这样就可以避免修改具体的实现类。
引入动态代理:
通过以上可以发现,每一个代理类只能为一个接口服务,这样程序开发中必然会产生许多代理类,此时可以使用一个代理类完成我那个全部的代理功能。。。
类结构:
package com.xiyou.dongtaidaili;
/* 代理模式
* 主题接口
*/
public interface Usermanager {
public final String userId=null, userName=null;
public void addUser(String userId, String userName);
public void deluser(String userId);
public String findUser (String userId);
public void modifyUser(String userId, String userName);
}
package com.xiyou.dongtaidaili;
/*
* 具体用户管理实现类 (实现 Usermanager接口)
*/
public class UserManagerImpl implements Usermanager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.adduser");
}
@Override
public void deluser(String userId) {
System.out.println("UserManagerImpl.deluser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.finduser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
动态代理类:
/*
* 代理类
* 采用JDK动态代理必须实现InvocationHandler接口 使用Proxy 类创建相应的代理类
* InvocationHandler 是代理实例的调用处理程序实现的接口,
* 每个代理类都具有一个关联的调用处理程序。
* 对代理实例调用方法时,将对方调用进行编码并将其指派到它的调用处理程序的invoke()方法中。
*/
public class ProxyHandler implements InvocationHandler {
private Object target;
public Object newProxyInstance(Object target) {
this.target = target;
/*
* newProxyInstance 返回一个指定接口的代理实例,该接口可以将方法代用指派到指定的调用处理程序。 loader -
* 定义代理类的类加载器 interfaces - 代理类要实现的接口列表 h - 指派方法调用的调用处理程序
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/*
* 反射 ,可以在不知道具体类的情下,根据配置的参数去调用一个类的方法, invoke(Object proxy, Method method,
* Object[] args) 在代理实例上处理方法调用并返回结果 proxy 在其上调用方法的代理实例。
* method:对应于在实例上处理的接口方法的Method实例。 arg:包含传入代理实例方法参数值的对应对象,
* 返回:从代理实例的方法调用返回的值。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("satrt------>" + method.getName());
for (int i = 0; i < args.length; i++) {
System.out.println("args=" + args[i]);
}
Object ret = null;
try {
// 调用目标方法
ret = method.invoke(target, args);
System.out.println("sucess------>" + method.getName());
} catch (Exception e) {
System.out.println("error---------->" + method.getName());
throw e;
}
return ret;
}
}
测试类:
public class Client {
public static void main(String[] args) {
ProxyHandler p = new ProxyHandler();
Usermanager usermanager = (Usermanager) p.newProxyInstance(new UserManagerImpl());
usermanager.addUser("04131145", "李四");
System.out.println(usermanager.findUser("04131145"));
System.out.println("client main =------>");
}
}
运行结果:
satrt------>addUser
args=04131145
args=李四
UserManagerImpl.adduser
sucess------>addUser
satrt------>findUser
args=04131145
UserManagerImpl.finduser
sucess------>findUser
张三
client main =------>
在AOP(面向切面编程):将日志,性能统计,安全控制,事件处理,异常处理等代码从业务逻辑代码中分离出来,通过这些行为的分离,我们希望可以将它们独立到非指导业务逻辑方法中,进而改变这些行为的时候不影响业务的逻辑的代码。
UserManagerImplProxy类中它的两个方法System.out.println("start-->addUser()")和System.out.println("success-->addUser()"),这是做核心动作之前和之后的两个截取段,正是这两个截取段,却是我们AOP的基础,在OOP里,System.out.println("start-->addUser()")、核心动作、System.out.println("success-->addUser()")这个三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如System.out.println("start-->addUser()")里做的可能是权限的判断,在所有类中它都是做权限判断,而在每个类里核心动作却各不相同,System.out.println("success-->addUser()")可能做的是日志,在所有类里它都做日志。正是因为在所有的类里,核心代码之前的操作和核心代码之后的操作都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析,设计和编码,这就是我们的AOP思想。一句话说,AOP只是在对OOP的基础上进行进一步抽象,使我们的类的职责更加单一。
总结:
代理对象就是被代理的对象包装一层,对齐内部做一些额外工作,比如用户无法访问外网,可以使用网络代理先翻墙,然后在访问外网。这就是代理的作用。