代理模式
代理模式 顾名思义就是为我们真正使用的对象寻找一个代理,我们通过调用代理对象的方法间接地调用真正使用的对象的方法。就像我们生活中去工厂打工,不直接去工厂,而去找中介、去旅游不直接去旅游目的地,而是去找旅行社一样。
代理模式的优点:
(1)代理模式能将代理对象与真实被调用目标对象分离。
(2)在一定程度上降低了系统的耦合性,扩展性好。
(3)可以起到保护目标对象的作用。
(4)可以增强目标对象的功能。
代理模式的缺点:
(1)代理模式会造成系统设计中类的数量增加。
(2)在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢。
(3)增加了系统的复杂度。
静态代理
静态代理的角色分工
- 代理类 和 实现类 公用的接口
- 实现类
- 代理类
如下,定义了查询数据库的接口UserDao,和UserDao的实现类UserDaoImp,以及代理类UserDaoProxy。我们同通过代理类UserDaoProxy给UserDaoImp的select方法增加一些之执行前后的处理。
public interface UserDao {
void select(String id);
}
public class UserDaoImp implements UserDao{
@Override
public void select(String id) {
System.out.println("已查询到id为"+id+"的用户信息");
}
}
public class UserDaoProxy implements UserDao{
private UserDao userDao;
// 前置方法增强
private void before(){
System.out.println("正在执行查询数据前的数据库连接操作");
}
// 后置方法增强
private void after(){
System.out.println("正在执行关闭数据库连接操作");
}
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void select(String id) {
before();
this.userDao.select(id);
after();
}
}
测试类
public class Test {
public static void main(String[] args) {
UserDaoProxy userDaoProxy = new UserDaoProxy(new UserDaoImp());
userDaoProxy.select("110");
}
}
动态代理
静态代理的方式需要我们手动地编写代理类的代码,如果是一两个还好,但如果有多个不同的类需要代理,静态方法就不太适合了,这时我们就可以用动态代理来实现。
动态代理类的角色分工
- 接口
- 实现类
- 生成代理对象的类
public interface UserDao {
void queryUserById(String id);
void queryUserByUsername(String username);
void removeUser(String id);
}
public class UserDaoImp implements UserDao {
@Override
public void queryUserById(String id) {
System.out.println("已查询到id为"+id+"的用户信息");
}
@Override
public void queryUserByUsername(String username) {
System.out.println("已查询到username为"+username+"的用户信息");
}
@Override
public void removeUser(String id) {
System.out.println("删除用户成功");
}
}
生成代理对象的类
public class CommonProxy implements InvocationHandler {
private Object target; // 保存被代理的对象的引用
public CommonProxy(Object target) {
this.target = target;
}
public Object newInstanse(){
Class<?> clazz = target.getClass();
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
Object obj = null;
// 只对query开头的方法代理
if(name.startsWith("query")){
before(); //前置增强
obj = method.invoke(this.target, args);
after(); //后置增强
}else{ // 其他方法不代理,直接调用
obj = method.invoke(this.target,args);
}
return obj;
}
private void before(){
System.out.println("执行查询操作前的准备");
}
private void after(){
System.out.println("执行查询操作后的准备");
System.out.println();
}
}
测试代码
public class Test {
public static void main(String[] args) {
UserDao userDao = (UserDao) new CommonProxy(new UserDaoImp()).newInstanse();
userDao.queryUserById("110");
userDao.queryUserByUsername("王五");
userDao.removeUser("120");
}
}
这样我们不仅可以代理UserDao的子类,也可以代理其他的类,只要被代理的目标类中含有query开头的方法,就能对该方法增强。
静态代理与动态代理的对比
(1)静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则。
(2)动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
(3)若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。
(4)动态代理用到反射技术,速度较慢。