一、什么是静态代理
静态代理是代理模式中的一种,静态跟动态相对,静态是指代理类在编译阶段生成,在程序运行之前就已经存在了,动态代理则是在运行期生成代理类。静态代理都是我们在Java代码中定义的。目前静态代理主要有AspectJ静态代理和JDK静态代理(动态代理有JDK动态代理和Cglib动态代理)
关系图:
二、AspectJ静态代理(相当是幕后代理)
AspectJ是一个Java实现的面向切面的框架,它拓展了Java语言。AspectJ有自定义的语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。这里说的“专门编译器”就可以看出AspectJ是典型的静态代理技术,因为是在编译时期就生成了代理类,而使用AspectJ也肯定需要指定特定的编译器。
使用静态代理模拟:
候选人-->人资-->技术经理,候选人想要应聘进入技术经理部门工作,需要经过代理人资的筛选才能推荐给技术经理。确定三者角色关系:候选人是客户,人资是代理方,技术经理是委托方,经理把发布需求人员招聘岗位信息,筛选简历,基本面谈等工作委托给人资,这样候选人想应聘这个岗位只需要向人资投递简历和沟通,这样经理也不用暴露身份,而人资也可以在审查简历和候选人沟通中告知候选人需要具备哪些技能,什么时候来面试,在哪面试等等,人资可以给多个候选人提供服务,这样候选人只需联系人资即可投递简历,应聘不同的岗位了,找个合适自己的部门岗位。下面代码模拟人资跟候选人的模型。
1.定义一个候选人需要具备能力的抽象接口(也就是经理(委托方)要求的条件)
/**
* @author huahua
* @date 2020/2/18
* @desc 所拥有技能抽象接口
*/
public interface TechnologyService {
void coding(String languageName);
void test();
}
2.定义候选人类并且实现上面的技能接口
/**
* @author huahua
* @date 2020/2/18
* @desc 候选人类实现TechnologyService接口,候选人需必会的技能
*/
public class Candidate implements TechnologyService {
private String name;
//1.硬编码能力
@Override
public void coding(String languageName) {
System.out.println(this.name+" 非常熟练使用"+languageName);
}
//2.测试能力
@Override
public void test() {
System.out.println(this.name+"精通测试!");
}
public Candidate() {
}
public Candidate(String name) {
this.name = name;
}
}
3.使用AspectJ语法定义一个代理人资
/**
*@author huahua
*@date 2020/2/18
*@desc 使用AspectJ语法实现一个代理
*/
public aspect AgentByAspectJ {
/**
* 1.定义切点,绑定候选人的方法
* */
pointcut checkPointCut():call(* Candidate.coding(..));
/**
* 2.定义前置通知
* before(参数):连接点函数{
* 函数体
* }
* */
before():checkPointCut(){
sendJianLi();
}
/**
* 3.定义后置通知
*
* */
after():checkPointCut(){
checkResult();
}
private void sendJianLi(){
System.out.println("请发送简历过来!");
}
private void checkResult(){
System.out.println("能力达不到要求,简历筛选不通过!out!!");
}
}
4.测试类
/**
* @author huahua
* @date 2020/2/18
* @desc 测试类
*/
public class Application {
public static void main(String[] args) {
Candidate candidate=new Candidate("huahua");
candidate.coding("java");
//candidate.test();
}
}
输出结果:
请发送简历过来!
huahua 非常熟练使用java
能力达不到要求,简历筛选不通过!out!!
可以看到Candidate的coding方法前后输出了我们 在AspectJ代理中定义的前置通知后后置通知,所以是AspectJ在编译期间,根据代码中定义的代码,生成了增强的Candidate类,而我们实际调用时,就会实现代理类的功能。具体的AspectJ语法我们不深究,只需要知道pointcut是定义代理要代理的切入点,before()和after()分别可以定义具体在切入点前后需要的额外操作。
总结一下,AspctJ就是用特定的编译器和语法,对类实现编译期增强,实现静态代理技术,下面我们看JDK静态代理。
三、JDK静态代理(相当是前台代理)
通常情况下,JDK静态代理更多的是一种设计模式,JDK静态代理的代理类和委托类会实现同一个接口或者是派生自相同的父类。
继续使用上面的例子完成JDK静态代理:
人资也实现了TechnologyService接口(也需要具备这方面的基础技能),持有了候选人的所有简历资料,在候选人来面试前后加上了人资需要处理的事情,如简历信息检查的真假性,能力是否达标等。
1.新增人资代理类HrAgent
package staticproxy.jdk;
import staticproxy.Candidate;
import staticproxy.TechnologyService;
/**
* @author huahua
* @date 2020/2/18
* @desc 人资类实现TechnologyService接口,需要具备比较基础的技能就好了。
*/
public class HrAgent implements TechnologyService {
private Candidate candidate;
public HrAgent() {
}
public HrAgent(Candidate candidate) {
this.candidate = candidate;
}
private void sendJianLi(){
System.out.println("请发送简历过来!");
}
private void checkResult(){
System.out.println("能力达不到要求,简历筛选不通过!out!!");
}
@Override
public void coding(String languageName) {
sendJianLi();
candidate.coding(languageName);
checkResult();
}
@Override
public void test() {
sendJianLi();
candidate.test();
checkResult();
}
}
2.测试
/**
* @author huahua
* @date 2020/2/18
* @desc JDK静态代理测试类
*/
public class Application {
public static void main(String[] args) {
//这位叫huahua123的候选人来人资面试了,先拿简历
HrAgent hrAgent=new HrAgent(new Candidate("huahua123"));
//人资面对面跟这为java候选人面试,根据候选人的具体情况来评判结果
hrAgent.coding("java");
}
}
输出结果:
请发送简历过来!
huahua123 非常熟练使用java
能力达不到要求,简历筛选不通过!out!!
总结:
上面的代理模式中,候选人类(委托类)必须是事先已经存在的,并将其作为人资类(代理类)对象的内部属性,使用的时候,必须一个候选人对应一个人资来进行筛选面试,如果很多不同类型的候选人就会导致类膨胀,此外,如果实现并不知道候选人是那种类型,即事先并不知道委托类是个什么人,人资该怎么代理呢,这就可以通过动态代理来解决这个问题了(就像人资可以通过招聘网站,让符合招聘类型的人自己来投递简历,然后就代理筛选,面试等一系列操作)
四、总结
上面的静态代理很简单也能说明问题,总结一下静态代理的优缺点:
优点:业务类可以只关注自身逻辑,通过代理类来增强通用的逻辑处理,可以重用
缺点:
- 一种代理对象的接口需要服务于一种类型的对象(就像要招聘技术岗位需要对应技术类型人员),如果需要代理的类很多,那就必须每个类都需要代理(就像一位人资需要为不同的经理招聘不同类型的候选人一样),规模一大了就不能胜任了。
- 如果接口(需求条件)增加了一个方法,除了所有实现(候选人)类需要实现这个方法外,所有代理类(人资)也要实现该方法,增加了代码的维护复杂度。