项目开发的问题
在直接访问对象时带来的问题
当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理
生活中如何解决类似问题
- 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
- 买火车票不一定在火车站买,也可以去代售点。
- 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
代理模式的设计思想
AOP设计思想
开闭原则
单一职责原则
依赖倒置原则
代理模式的具体实现
这种类型的设计模式属于结构型模式。
在代理模式中,一个类代表另一个类的功能。
业务类只需要关注业务逻辑本身,保证了业务类的重用性。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
代理模式代码的主要特点是:不改变原有类的前提下,在原有类某些方法执行前后,插入任意代码。所以代理模式需要写新的类对原有的类进行包装。代理模式目前实现的方式有三种:
- 静态代理
- 动态代理
- Cglib代理
- Spring AOP
三大代理模式解析
静态代理:
需要增强原有类的哪个方法,就需要对在代理类中包装哪个方法。个人理解,从功能上来说,原有类和代理类不一定要实现共同接口,但是为了赋予代理和和被代理类之间的逻辑关系,增加程序的可读性,可理解性,逻辑性,增加代理对象和被代理对象之间的关系,以更加符合面向对象编程是思维,而应该实现共同接口。
接口
public interface Account {
// 查看账户方法
void queryAccount();
// 修改账户方法
void updateAccount();
}
接口实现类
public class AccountImpl implements Account {
@Override
public void queryAccount() {
System.out.println("查看账户方法...");
}
@Override
public void updateAccount() {
System.out.println("修改账户方法...");
}
}
接口代理类
public class AccountProxy implements Account {
private AccountImpl accountImpl;
/**
* 在代理类内部初始化委托类
*
* @param accountImpl 委托类
*/
AccountProxy(AccountImpl accountImpl) {
this.accountImpl = accountImpl;
}
@Override
public void queryAccount() {
//事物处理之前的代码
System.out.println("事务处理之前");
// 调用委托类的方法;
accountImpl.queryAccount();
//事物处理之后的代码
System.out.println("事务处理之后");
}
@Override
public void updateAccount() {
//事物处理之前的代码
System.out.println("事务处理之前");
// 调用委托类的方法;
accountImpl.updateAccount();
//事物处理之后的代码
System.out.println("事务处理之后");
}
}
测试入口
public class AccountProxyTest {
@Test
public void queryCount() {
AccountImpl countImpl = new AccountImpl();
AccountProxy countProxy = new AccountProxy(countImpl);
countProxy.queryAccount();
}
@Test
public void updateCount() {
AccountImpl countImpl = new AccountImpl();
AccountProxy countProxy = new AccountProxy(countImpl);
countProxy.updateAccount();
}
}
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时比较繁琐了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
所以我们又引入了动态代理来解决这些问题
动态代理:
使用反射机制,方法和对象都是传入的变量,就可以经过传入的对象和方法而动态调用被代理对象的任何方法,jdk中提供了实现此动态代理的API,被代理类必须实现接口
相比于静态代理,我们只需要修改代理类就可以实现动态代理
接口代理类(动态)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理
* 1. 可以同意定义所有的方法执行前后的动作
* 2. 只需要继承 InvocationHandler 接口
*/
public class AccountProxyDynamic implements InvocationHandler {
private Object object;//用于接收具体实现类的实例对象
//使用带参数的构造器来传递具体实现类的对象
AccountProxyDynamic(Object obj) {
this.object = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理前");
method.invoke(object, args);
System.out.println("动态代理后");
return null;
}
}
测试入口
import com.mingyuan.summer.study.pattern.proxy1.Account;
import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class AccountProxyDynamicTest {
@Test
public void invoke() {
Account accountImpl = new AccountImpl();
InvocationHandler invocationHandler = new AccountProxyDynamic(accountImpl);
Account accountProxyDynamic = (Account) Proxy.newProxyInstance(Account.class.getClassLoader(), new Class[]{Account.class}, invocationHandler);
accountProxyDynamic.queryAccount();
accountProxyDynamic.updateAccount();
}
}
cglib代理
返回对象是代理对象的子类,不需要代理对象实现接口。当调用原对象方法时,实际上调用的是代理子类的方法。
Spring AOP
代理模式的使用场景
- Windows 里面的快捷方式。
- Spring AOP:即面向切面编程。在不改变原来代码的前提下,也不对源代码做任何协议接口要求。而实现了类似插件的方式,来修改源代码,给源代码插入新的执行代码。Spring中的赖加载都是用代理模式实现
- 项目日志功能