相关笔记(先学maven,一下项目都是通过maven创建的)
1.代理模式
目标对象不可访问,通过代理对象进行访问,而且增强式的访问,适当进行业务的扩展
生活中:
房东 ===>目标对象
房屋中介 ===>代理对象
你,我 ===>客户端对象
服务生产厂 ===>目标对象
门店(旗舰店) ===>代理对象
你,我 ===>客户端
开发中:
运营商(电信,移动,联通) ====>目标对象
第三方公司 ====>代理对象
开发的应用程序需要发送短信的功能 ===>客户端
开发的应用程序需要支付的功能
2.代理模式的作用
-
控制目标对象的访问
-
增强功能(主要功能由目标对象实现,附加功能由代理对象实现)
3.代理模式的分类
-
静态代理
-
动态代理,又分为JDK动态代理和 CGLib动态代理(子类代理)
4.什么是静态代理
它是代理模式的一种.
它具备以下特点:
- 目标对象和代理对象实现同一个业务接口
- 目标对象必须实现接口
- 代理对象在程序运行前就已经存在
- 能够灵活的进行目标对象的切换,却无法进行功能的灵活处理(使用动态代理解决此问题)
5.静态代理实现
业务功能:请明星进行节目表演.
-
明星刘德华:目标对象(无法直接访问)
-
刘德华助理:代理对象(我们可以访问,他可以跟明星对接)
-
我们:客户端对象
代码实现
通过maven创建 java工程:quickstart(具体见maven笔记)
-
业务接口Service
public interface Service { //规定的唱歌的业务功能 void sing(); }
-
目标对象:
/** 目标对象:刘德华, 周润发。实现业务接口中的功能,进行唱歌表演 */ public class SuperStarLiu implements Service { @Override public void sing() { System.out.println("我是刘德华,我正在表演唱歌............"); } } public class SuperStartZhou implements Service { public void sing() { System.out.println("周润发正在唱歌"); } }
-
代理对象:
public class Agent implements Service { //类中的成员变量设计为接口 public Service target; //目标对象 //传入目标对象,方法的参数设计为接口(实现多态,以后不管目标对象有多少个都能用一行代码实现,不必单独为每个目标对象创建实例) public Agent(Service target){ this.target = target; } @Override public void sing() { System.out.println("预订时间.........."); System.out.println("预订场地.........."); //切记切记:业务功能必须由目标对象亲自实现 // SuperStarLiu liu = new SuperStarLiu(); // liu.sing(); // // SuperStarZhou zhou = new SuperStarZhou(); // zhou.sing(); //面向接口编程:调用时,接口指向实现类 target.sing(); System.out.println("结算费用.........."); } }
-
客户端对象
public class MyTest{ @Test public void testAgent1(){ //测试功能 // SuperStarLiu liu = new SuperStarLiu(); // liu.sing(); // Agent agent = new Agent(); // agent.sing(); //有接口和实现类,必须使用接口指向实现类(规范) Service agent = new Agent(); agent.sing(); } @Test public void testAgent2(){ //测试功能 // SuperStarLiu liu = new SuperStarLiu(); // liu.sing(); // Agent agent = new Agent(); // agent.sing(); //有接口和实现类,必须使用接口指向实现类(规范) Service agent = new Agent(new SuperStartZhou()); //Service agent = new Agent(new SuperStartLiu()); agent.sing(); } }
6.面向接口编程(重要)
类中的成员变量设计为接口
方法的形参设计为接口
方法的返回值设计为接口
调用时接口指向实现类(多态,动态绑定)【父类指向子类 father a = new son();】
===》如第5节中的 Service 接口
【在项目中表现为多建了一个 SuperStarZhou 类实现 Service接口,如果要调用该类的对象,必须在代理对象中 new 该类对象,这样没办法 灵活的进行目标对象的切换,于是就采取了上述的步骤,得到新的 代理对象 代码如第5节】
7.动态代理
静态代理的弊端:当 我们在 Service 接口中添加新的功能(方法)时,所有实现该接口的类必须重写该方法(目标对象,代理对象),则 就需要更改很多地方,程序失去了灵活性。所以我们需要动态代理
代理对象在程序运行的过程中动态在内存构建。可以灵活的进行业务功能的切换.
8.JDK动态代理
- 目标对象必须实现业务接口(与静态代理相同)
- JDK代理对象不需要实现业务接口(与静态代理不同)
- JDK动态代理的对象在程序运行前不存在,在程序运行时动态的在内存中构建
- JDK动态代理能够灵活的进行业务功能的切换
- 目标对象类中的方法(非接口中的方法)不能被代理
9.JDK动态代理用到的类和接口
它是使用现在的工具类完成JDK动态代理的实现.
- Proxy 生成动态代理对象.
- Method 进行目标对象的方法的反射调用
- InvocationHandle 实现代理和业务功能的
-
Proxy类
它是java.lang.reflect.Proxy包下的类.
它有一个方法Proxy.newProxyInstance(…)专门用来生成动态代理对象.
public static Object newProxyInstance(ClassLoader loader, // 类加载器 Class<?>[] interfaces, // 目标对象实现的所有接口,泛型数组 InvocationHandler h // 它就类似于Agent的功能,代理的功能和目标对象的业务功能调用在这 )throws IllegalArgumentException{...}
-
Method类
反射用的类,用来进行目标对象的方法的反射调用.method对象接住我们正在调用的方法sing(),show()
method == sing(),show()
method.invoke();==>手工调用目标方法 sing(); show();
-
InvocationHandler接口
它是实现代理和业务功能的。我们在调用时使用匿名内部实现.
接口的匿名内部实现:
@Test
public testA(){
Service service = new Servcie(){
@Override
public void sing(){
System.out.println("哈哈哈哈....");
}
};
service.sing();
}
代码实现:
-
业务接口
public interface Service { void sing(); String show(int age); }
-
目标对象
public class SuperStartLiu implements Service { public void sing() { System.out.println("我是刘德华,我正在唱歌。。。。"); } public String show(int age) { System.out.println("刘德华的show。。。。" + age); return "liu"; } // 此方法不能被代理(不能增强功能) public void one() { System.out.println("one......."); } } public class SuperStartZhou implements Service { public void sing() { System.out.println("我是周润发,我正在唱歌。。。。"); } public String show(int age) { System.out.println("周润发的show。。。。" + age); return "zhou"; } }
-
获取 动态代理对象,进行封装是因为 返回动态代理对象 的代码太繁杂,在客户端获取不美观
public class ProxyFactory { // 类中的成员变量设计为接口,目标对象 Service target; // 传入目标对象 public ProxyFactory(Service target){ this.target = target; } // 返回动态代理对象 public Object getAgent(){ return Proxy.newProxyInstance( // ClassLoader loader, 类加载器,完成目标对象的加载 target.getClass().getClassLoader(), // Class<?>[] interfaces,目标对象实现的所有接口 target.getClass().getInterfaces(), // InvocationHandler h,实现代理功能的接口 ,我们传入的是匿名内部实现 new InvocationHandler() { @Override public Object invoke( Object proxy, // 创建代理对象 Method method, // method就是目标方法sing(),show() Object[] args // 目标方法的参数 ) throws Throwable { // 代理功能 System.out.println("预订时间........"); // 代理功能 System.out.println("预订场地........"); // 主业务功能实现 // target.sing();还是写死了方法的调用, 不成 // sing(),show(),one() Object obj = method.invoke(target,args); // 代理功能 System.out.println("结算费用........"); return obj; //切记:这个是目标方法的返回值 } } ); } }
-
客户端对象
public class MyTest { @Test public void testJDK() { // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); // factoty.getAgent(); 的返回值是 Object,无法直接用它调用sing方法 Service agent = (Service) factoty.getAgent(); agent.sing(); } @Test public void testJDK1() { // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); Service agent = (Service) factoty.getAgent(); String s = agent.show(66); System.out.println(s); } @Test public void testObject() { ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); Service agent = (Service) factoty.getAgent(); // class com.sun.proxy.$Proxy4 代理对象类型(运行时类型) System.out.println(agent.getClass()); Service liu = new SuperStartLiu(); System.out.println(liu.getClass()); } @Test public void testJDK2() { // ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); ProxyFactoty factoty = new ProxyFactoty(new SuperStartLiu()); // one方法是 子类 独有的 // Service agent = (Service) factoty.getAgent(); // agent.one(); // 类型转换异常 // SuperStartLiu agent = (SuperStartLiu) factoty.getAgent(); // agent.one(); } }
使用动态代理,在Service中新增业务功能不用更改 ProxyFactoty类中的代码;而静态代理中,需要更改代理对象中的代码。
10.CGLib动态代理(用的少)
又称为子类。通过动态的在内存中构建子类对象,重写父类的方法进行代理功能的增强.
如果目标对象没有实现接口,则只能通过CGLib子类代理来进行功能增强.
子类代理是对象字节码框架ASM来实现的.
项目路径【自制的一个简单的CGLib代理】
注意:
-
被代理的类不能为final, 否则报错.
-
目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
public Object getProxyInstance(){
//1.使用工具类
Enhancer en=new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理)对象
return en.create(); ===>返回的是子类代理对象
}
**
子类代理是对象字节码框架ASM来实现的.
项目路径【自制的一个简单的CGLib代理】
注意:
-
被代理的类不能为final, 否则报错.
-
目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法。
public Object getProxyInstance(){
//1.使用工具类
Enhancer en=new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理)对象
return en.create(); ===>返回的是子类代理对象
}