-
- (Singleton)
单例模式(Singleton Pattern):是指确保一个类在任何情况下都绝对只有一个实例。单例模式可减少内存开销。
饿汉单例模式(无法做到延迟加载) | 懒汉单例模式(注意使用双重校验锁解决线程安全问题和性能问题) |
public class HungrySingleton { | public class Single { // 懒汉式 /** 定义单例属性(私有静态) */ private static Single s; //私有化构造函数禁止外部调用并且防止反射破坏单例。 private Single() { if (s != null) { throw new RuntimeException("不允许创建多个单例!"); } } /** 提供全局唯一访问点(公共静态),初次调用时才创建单例对象*/ public static Single getInstance() { //5. 锁外再加判断解决性能问题 if (s == null) { //4. 加锁解决线程安全问题 synchronized (Singleton.class) { if (s == null) { s = new Single(); } } } return s; }
|
静态内部类懒汉单例模式(使用静态内部类来维护外部类单例,只有调用内部类获取单例的方法时才会加载内部类,从而初始化外部类单例) | 枚举饿汉单例模式(不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象) |
/** | /** |
参考:你说你是资深程序员了,那这几种单例模式常见的写法你都知道吗?建议收藏_民工码农程序员的博客-CSDN博客
-
- (Strategy)(eg:集合之比较器、File之过滤器)
含义:
策略模式(Strategy Pattern):定义一组算法,将每个算法各自封装到具有共同接口或共同抽象父类的独立类中,使它们之间可以相互替换。策略模式使这些算法在客户端变换调用它们的时候能够在不影响客户端和环境的情况下发生变化。
代表:例如TreeSet和TreeMap中带Comparator接口引用参数的构造方法可传入不同的比较器算法实现类来对元素进行排序。
策略模式使开发人员能够开发出许多可替换的部分组成的软件,并且各部分之间是弱连接的关系。
组成:
抽象策略角色:策略类,通常由一个接口或者抽象类担当。如Comparator接口。
具体策略角色:包装了相关的算法和行为。如ComparatorImpl实现类。
环境角色:持有一个策略类的引用,最终给客户端调用的。如TreeSet和TreeMap带参构造方法中的Comparator接口引用参数。
编程步骤:
1、对策略对象定义一个公共接口。
2、编写一组各自独立的策略类,且都实现上述公共接口。
3、在使用策略对象的类中保存一个对策略对象的接口类引用。
4、在使用策略对象的类中实现对策略引用的setter和getter方法(注入)或者使用构造方法完成赋值。
5、在客户端中创建使用策略对象的类并传入策略实现类对象。
案例:加减乘除运算的策略模式
public class Environment { private Strategy strategy; public Strategy getStrategy() { return strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public Environment(Strategy strategy) { this.strategy = strategy; } public int calculate(int a, int b) { return strategy.calculate(a, b); } } | public interface Strategy { public int calculate(int a, int b); } |
public class AddStrategy implements Strategy { @Override public int calculate(int a, int b) { return a + b; } } | |
public class SubtractStrategy implements Strategy { @Override public int calculate(int a, int b) { return a - b; } } | |
public class Client { public static void main(String[] args) { Environment e = new Environment(new AddStrategy()); System.out.println(e.calculate(3, 4)); e.setStrategy(new SubtractStrategy()); System.out.println(e.calculate(3, 4)); e.setStrategy(new MultiplyStrategy()); System.out.println(e.calculate(3, 4)); e.setStrategy(new DivideStrategy()); System.out.println(e.calculate(3, 4)); } } | public class MultiplyStrategy implements Strategy { @Override public int calculate(int a, int b) { return a * b; } } |
public class DivideStrategy implements Strategy { @Override public int calculate(int a, int b) { return a / b; } } |
缺点和解决方案:
缺点:
1、客户端必须知道所有具体的策略类,并自行决定使用哪一个策略类。
2、造成很多策略类。
解决方案:采用工厂模式。
-
- (Observer)(eg:GUI之事件监听)
含义:(以awt事件监听处理机制为例)
观察者模式定义了一种一对多(按钮对多个事件监听器)的依赖关系,让多个观察者(事件监听器)对象同时监听某一个主题对象(按钮)。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。(我动你也动!)
组成:
抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类或接口来实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。通常用一个子类实现。
实质:具体主题角色保存了具体观察者角色的引用集成员变量,自身变化时调用集合中所有具体观察者角色的更新方法,使得各个观察者角色都更新。
自定义观察者模式代码实例:
/** * 抽象主题角色 */ interface Watched { public void addWatcher(Watcher watcher); public void removeWatcher(Watcher watcher); public void notifyWatchers(String message); } | /** * 抽象观察者角色 */ interface Watcher { public void update(String message); } |
/** * 具体主题角色 */ class Girl implements Watched { private List<Watcher> boys = new ArrayList<Watcher>(); @Override public void addWatcher(Watcher watcher) { boys.add(watcher); } @Override public void removeWatcher(Watcher watcher) { boys.remove(watcher); } @Override public void notifyWatchers(String message) { System.out.println("girl: " + message); for (Watcher boy : boys) { //具体主题角色调用具体观察者角色的更新方法 boy.update(message); } } } | /** * 具体观察者角色 */ class Boy implements Watcher { @Override public void update(String message) { if ("约?".equals(message)) { System.out.println("boy: 约!"); } } } |
/** * 测试 */ public class TestObserver { public static void main(String[] args) { Girl girl = new Girl(); girl.addWatcher(new Boy()); girl.addWatcher(new Boy()); girl.addWatcher(new Boy()); girl.notifyWatchers("约?"); } } |
使用JDK提供的观察者模式API:
/** * 测试 */ public class UseObserverPatternFromJDK { public static void main(String[] args) { Lady lady = new Lady(); lady.addObserver(new Guy1()); lady.addObserver(new Guy2()); lady.date("约?"); } } | import java.util.Observer; /** * 具体观察者角色1 */ class Guy1 implements Observer { @Override public void update(Observable o, Object arg) { if ("约?".equals(arg)) { System.out.println("guy1: 约!"); } } } |
import java.util.Observable; /** * 具体主题角色 */ class Lady extends Observable { public void date(String message) { System.out.println("lady: " + message); setChanged();//关键代码1 notifyObservers(message); //关键代码2 } } | import java.util.Observer; /** * 具体观察者角色2 */ class Guy2 implements Observer { @Override public void update(Observable o, Object arg) { if ("约?".equals(arg)) { System.out.println("guy2: 约!"); } } } |
-
- (Proxy)(eg:Spring之AOP)
含义:
代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
开闭原则:软件实体应当对扩展开放,对修改关闭。也就是说,软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即修改原有的代码对外部的使用是透明的。
静态代理模式(代理类编译期生成)
组成:
抽象角色:声明代理对象和真实对象的共同接口或父类。
代理角色:代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象拥有与真实对象相同的接口或父类以便在任何时刻都能代替真实对象。代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
代码示例:
/** * 客户端 */ public class TestProxy { public static void main(String[] args) { //构造代理对象(内含真实对象引用)并调用其代理方法 new ProxySubject().request(); } } | /** * 代理角色 */ public class ProxySubject extends Subject { private RealSubject realSubject;//对真实角色的引用 @Override public void request() { this.preRequest();//执行真实对象方法前可加入其它业务逻辑 if (null == realSubject) { realSubject = new RealSubject(); } realSubject.request();///执行真实对象方法 this.postRequest();//执行真实对象方法后也可加入其它业务逻辑 } private void preRequest() { System.out.println("看房费一千块!"); } private void postRequest() { System.out.println("中介费五千块!"); } } |
/** * 抽象角色 */ public abstract class Subject { public abstract void request(); } | |
/** * 真实角色 */ public class RealSubject extends Subject { @Override public void request() { System.out.println("总统套房出租一万块!"); } } |
缺点:一个真实角色对应一个代理角色,大量使用将导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?使用动态代理!
动态代理模式(代理类运行期生成)
组成:
抽象角色(共同接口):代理对象和真实对象的共同接口。
代理角色(代理类):运行时由Proxy类运用反射机制动态临时生成。
代理角色管理器(调用管理器):实现InvocationHandler接口。
真实角色(委托类):代理角色所代表的真实对象,是我们最终要引用的对象。
编程步骤:
1)声明抽象角色接口。
2)声明真实角色实现类。
3)声明一个实现InvocationHandler接口的动态代理管理器类。
4)在动态代理管理器类中声明Object类型的引用和带该引用参数的构造方法,用来接收真实角色对象。
5)实现InvocationHandler接口的invoke方法,在该方法中调用将在客户端传入的真实对象的方法:method.invoke(抽象角色接口引用, args);
6)在客户端里构造代理管理器对象,并传入真实对象。
7)使用Proxy类的静态方法 newProxyInstance(...) 生成动态代理角色对象,方法参数是:类装载器、真实对象所实现的接口 和 代理管理器。(代理对象将实现传入的真实对象所实现的接口,所以可用接口的引用接收返回的动态代理对象)
8)调用生成的代理对象的代理方法(真实对象有什么方法,代理对象就有什么代理方法),代理对象的代理方法将调用代理管理器的invok(...)方法,代理管理器的invok(...)方法将调用真实对象的与代理对象相对应的同名方法。
原理(运行过程):(双重代理,连环调用)
1)由Proxy类运用反射机制在运行时临时生成动态代理角色,创建该角色需要指定真实角色的类加载器、真实角色所实现的接口、调用管理器实例。
2)运行动态代理角色的代理方法,该代理方法会调用调用管理器(InvocationHandler接口的实现)的invoke方法,
3)调用管理器的invoke方法调用真实角色的真实方法,在调用前后可加入其它业务逻辑。
优点:抽象角色接口、真实角色实现类 和 动态代理管理器类 可以在运行时动态地改变。
缺点:只能代理实现了接口的委托类、
使用场合:调试、远程方法调用(RMI)、Spring之AOP
代码示例:
package com.tongwx.reflect; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; | /** * 抽象角色 */ public interface Subject { public void request(); } | /** * 真实角色 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("总统套房出租一万块!"); } } |
/** * 调用管理器,其invoke(...)方法将被代理角色重写 */ public class DynamicProxyHandler implements InvocationHandler { //任何真实对象的Object类引用(因为动态代理编译时不知道要传入哪个真实对象,所以类型设置为Object) private Object subject; //构造代理管理器时将传入真实对象 public DynamicProxyHandler(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { this.preRequest();//执行传入的真实对象方法前可加入其它业务逻辑 method.invoke(subject, args);//执行传入的真实对象的方法 this.postRequest();//执行传入的真实对象方法后也可加入其它业务逻辑 return null; } private void preRequest() { System.out.println("看房费一千块!"); } private void postRequest() { System.out.println("中介费五千块!"); } } | ||
/** * 客户端 */ public class TestDymamicProxy { public static void main(String[] args) { //构造真实对象 RealSubject realSubject = new RealSubject(); //构造代理管理器并传入真实对象 InvocationHandler handler = new DynamicProxyHandler(realSubject); //生成代理对象(传入任意类的类装载器、真实对象所实现的接口 和 调用管理器) //代理对象将实现传入的真实对象所实现的接口,所以可用接口的引用subject接收返回的代理对象 Subject dynamicProxySubject = (Subject) Proxy.newProxyInstance( realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); //调用代理对象的代理方法request(), //代理对象的代理方法request()将调用调用管理器的invok(...)方法, //代理管理器的invok(...)方法将调用真实对象的request()方法,还可以添加其他业务逻辑 dynamicProxySubject.request(); System.out.println("中介" + dynamicProxySubject.getClass() + "真是坑!");//class com.tongwx.reflect.$Proxy0 } } |
-
- 代理
CGLIB介绍与原理(部分节选自网络)
一、什么是CGLIB
CGLIB是一个功能强大,高性能的代码生成包。它可以为没有实现接口的类提供代理,
JDK的动态代理是通过反射类Proxy生成代理以及InvocationHandler回调接口实现的,它只能代理实现了接口的类,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
二、CGLIB原理
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB缺点:对于final方法,无法进行代理。
三、CGLIB的应用
广泛的被许多AOP的框架使用,例如Spring AOP和dynaop。Hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联。
四、CGLIB的API
1、Jar包:
cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.
2、CGLIB类库:
由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。
本系列使用的CGLIB版本是2.2。
net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。
net.sf.cglib.transform:编译期或运行期类和类文件的转换
net.sf.cglib.proxy:实现创建代理和方法拦截器的类
net.sf.cglib.reflect:实现快速反射和C#风格代理的类
net.sf.cglib.util:集合排序等工具类
net.sf.cglib.beans:JavaBean相关的工具类
本实战系列主要涉及的包:net.sf.cglib.proxy、net.sf.cglib.reflect、net.sf.cglib.util、net.sf.cglib.beans
通过方法拦截器MethodInterceptor和字节码增强器Enhancer实现动态代理
1、被代理类:
首先,定义一个类,该类没有实现任何接口,包含两个方法。
- public class ConcreteClassNoInterface {
- public String getConcreteMethodA(String str){
- System.out.println("ConcreteMethod A ... "+str);
- return str;
- }
- public int getConcreteMethodB(int n){
- System.out.println("ConcreteMethod B ... "+n);
- return n+10;
- }
- }
2、方法拦截器MethodInterceptor接口实现:
定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
- public class ConcreteClassInterceptor implements MethodInterceptor{
- public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
- System.out.println("Before:"+method);
- Object object=proxy.invokeSuper(obj, arg);
- System.out.println("After:"+method);
- return object;
- }
- }
参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
返回:从代理实例的方法调用返回的值。
其中,proxy.invokeSuper(obj,arg):
调用代理类实例上的proxy方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)
在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。
3、使用字节码增强器Enhancer生成动态代理类:
- Enhancer enhancer=new Enhancer();
- enhancer.setSuperclass(ConcreteClassNoInterface.class);
- enhancer.setCallback(new ConcreteClassInterceptor());
- ConcreteClassNoInterface ccni=(ConcreteClassNoInterface)enhancer.create();
这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。
首先将被代理类ConcreteClassNoInterface设置成父类,然后设置拦截器ConcreteClassInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型ConcreteClassNoInterface。
最后,在代理类上调用方法:
- ccni.getConcreteMethodA("shensy");
- ccni.getConcreteMethodB(0);
查看控制台输出:
- Before :public java.lang.String generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodA(java.lang.String)
- ConcreteMethod A ... shensy
- After :public java.lang.String generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodA(java.lang.String)
- Before :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)
- ConcreteMethod B ... 0
- After :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)
可以看到,拦截器在调用被代理类方法前后都执行了print操作。
通过回调过滤器CallbackFilter和字节码增强器Enhancer实现动态代理
本篇介绍回调过滤器CallbackFilter。
一、作用
在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效。
二、示例
1、被代理类:
首先定义一个被代理类,包含3个方法:
- public class ConcreteClassNoInterface {
- public String getConcreteMethodA(String str){
- System.out.println("ConcreteMethod A ... "+str);
- return str;
- }
- public int getConcreteMethodB(int n){
- System.out.println("ConcreteMethod B ... "+n);
- return n+10;
- }
- public int getConcreteMethodFixedValue(int n){
- System.out.println("getConcreteMethodFixedValue..."+n);
- return n+10;
- }
- }
2、回调过滤器CallbackFilter接口实现:
- public class ConcreteClassCallbackFilter implements CallbackFilter{
- public int accept(Method method) {
- if("getConcreteMethodB".equals(method.getName())){
- return 0;//Callback callbacks[0]
- }else if("getConcreteMethodA".equals(method.getName())){
- return 1;//Callback callbacks[1]
- }else if("getConcreteMethodFixedValue".equals(method.getName())){
- return 2;//Callback callbacks[2]
- }
- return 1;
- }
- }
其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引(见下文)。
3、使用字节码增强器Enhancer生成动态代理类:
- Enhancer enhancer=new Enhancer();
- enhancer.setSuperclass(ConcreteClassNoInterface.class);
- CallbackFilter filter=new ConcreteClassCallbackFilter();
- enhancer.setCallbackFilter(filter);
- Callback interceptor=new ConcreteClassInterceptor();//(1)
- Callback noOp=NoOp.INSTANCE;//(2)
- Callback fixedValue=new ConcreteClassFixedValue();//(3)
- Callback[] callbacks=new Callback[]{interceptor,noOp,fixedValue};
- enhancer.setCallbacks(callbacks);
- ConcreteClassNoInterface proxyObject=(ConcreteClassNoInterface)enhancer.create();
- //...见下文
生成代理类前,设置了CallbackFilter,上文中ConcreteClassCallbackFilter实现类的返回值对应Callback[]数组中的位置索引。此处包含了CGLib中的3种回调方式:
(1)MethodInterceptor:方法拦截器,上一篇文章中已经详细介绍过,此处不再赘述。
(2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。
(3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
其中,ConcreteClassFixedValue类实现如下:
- public class ConcreteClassFixedValue implements FixedValue{
- public Object loadObject() throws Exception {
- System.out.println("ConcreteClassFixedValue loadObject ...");
- Object object=999;
- return object;
- }
- }
该类实现FixedValue接口,同时锁定回调值为999(整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。
下面进行验证:
- //接上文...
- System.out.println("*** NoOp Callback ***");
- proxyObject.getConcreteMethodA("abcde");
- System.out.println("*** MethodInterceptor Callback ***");
- proxyObject.getConcreteMethodB(1);
- System.out.println("*** FixedValue Callback ***");
- int fixed1=proxyObject.getConcreteMethodFixedValue(128);
- System.out.println("fixedValue1:"+fixed1);
- int fixed2=proxyObject.getConcreteMethodFixedValue(256);
- System.out.println("fixedValue2:"+fixed2);
控制台输出:
- *** NoOp Callback ***
- ConcreteMethod A ... abcde
- *** MethodInterceptor Callback ***
- Before :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)
- ConcreteMethod B ... 1
- After :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)
- ConcreteClassInterceptor end...
- *** FixedValue Callback ***
- ConcreteClassFixedValue loadObject ...
- fixedValue1:999
- ConcreteClassFixedValue loadObject ...
- fixedValue2:999
getConcreteMethodA对应CallbackFilter中定义的索引1,在Callback[]数组中使用的过滤为NoOp,因此直接执行了被代理方法。
getConcreteMethodB对应CallbackFilter中定义的索引0,在Callback[]数组中使用的过滤为MethodInterceptor,因此执行了方法拦截器进行拦截。
getConcreteMethodFixedValue对应CallbackFilter中定义的索引2,在Callback[]数组中使用的过滤为FixedValue,因此2次赋值128和256的调用其结果均被锁定为返回999。
通过延迟加载器LazyLoader和字节码增强器Enhancer实现动态代理
LazyLoader接口继承了Callback,因此也算是CGLib中的一种Callback类型。
二、示例:
1、被代理类:
首先定义一个实体类LoaderBean,该Bean内有一个需要延迟加载的属性PropertyBean。
- public class LoaderBean {
- private String loaderName;
- private int loaderValue;
- private PropertyBean propertyBean;
- public LoaderBean(){
- this.loaderName="loaderNameA";
- this.loaderValue=123;
- this.propertyBean=createPropertyBean();
- }
- protected PropertyBean createPropertyBean(){
- Enhancer enhancer=new Enhancer();
- enhancer.setSuperclass(PropertyBean.class);
- return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
- }
- //setter/getter...
- }
PropertyBean.java:
需要延迟加载的类:
- public class PropertyBean {
- private String propertyName;
- private int propertyValue;
- //setter/getter
- }
在LoaderBean的构造方法中,对属性Bean进行了代理类生成,使用了CGLib中的LazyLoader回调接口。
- public class ConcreteClassLazyLoader implements LazyLoader{
- public Object loadObject() throws Exception {
- System.out.println("LazyLoader loadObject() ...");
- PropertyBean bean=new PropertyBean();
- bean.setPropertyName("lazy-load object propertyName!");
- bean.setPropertyValue(11);
- return bean;
- }
- }
验证延迟加载:
- LoaderBean loader=new LoaderBean();
- System.out.println(loader.getLoaderName());
- System.out.println(loader.getLoaderValue());
- PropertyBean propertyBean=loader.getPropertyBean();//访问延迟加载对象
- System.out.println(propertyBean.getPropertyName());
- System.out.println(propertyBean.getPropertyValue());
- System.out.println("after...");
- //当再次访问延迟加载对象时,就不会再执行回调了
- System.out.println(propertyBean.getPropertyName());
控制台输出:
- loaderNameA
- 123
- LazyLoader loadObject() ...
- lazy-load object propertyName!
- 11
- after...
- lazy-load object propertyName!
注意,第一次获取property bean的属性时,会触发代理类回调方法。第二次再获取property bean的属性时,就直接返回属性值而不会再次触发代理类回调方法了。
可见,延迟加载原理:
对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。
在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法,就会自动触发代理类回调)。
通过改进版延迟加载器Dispatcher和字节码增强器Enhancer实现动态代理
一、作用:
上一篇文章中,介绍了延迟加载器LazyLoader。本篇介绍另一种延迟加载接口Dispatcher。
Dispatcher接口同样继承于Callback,也是一种回调类型。
但是Dispatcher和LazyLoader的区别在于:LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。
二、示例:
与上篇文章相同,先定义一个实体类DispatcherBean,该Bean内有一个需要延迟加载的属性PropertyBean。PropertyBean的代码与上一篇相同,这里只看一下DispatcherBean:
- public class DispatcherBean {
- private String name;
- private String value;
- private PropertyBean propertyBean;
- public DispatcherBean(){
- this.name="DispatcherBean";
- this.value="abc";
- this.propertyBean=createDispatcherBean();
- }
- protected PropertyBean createDispatcherBean(){
- Enhancer enhancer=new Enhancer();
- enhancer.setSuperclass(PropertyBean.class);
- return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassDispatcher());
- }
- //setter/getter
- }
同样对propertybean生成了代理类,回调类ConcreteClassDispatcher实现了Dispatcher接口,如下:
- public class ConcreteClassDispatcher implements Dispatcher{
- public Object loadObject() throws Exception {
- System.out.println("Dispatcher loadObject ...");
- PropertyBean object=new PropertyBean();
- object.setPropertyName("PropertyBeanName!");
- object.setPropertyValue(1);
- return object;
- }
- }
验证延迟加载:
- DispatcherBean dispatcherBean=new DispatcherBean();
- System.out.println(dispatcherBean.getName());
- System.out.println(dispatcherBean.getValue());
- PropertyBean pb=dispatcherBean.getPropertyBean();
- System.out.println(pb.getPropertyName());
- //在每次访问时都要进行回调
- System.out.println(pb.getPropertyValue());
控制台输出:
- DispatcherBean
- abc
- Dispatcher loadObject ...
- PropertyBeanName!
- Dispatcher loadObject ...
- 1
由此可见,每次获取property bean的属性都会自动触发回调方法加载对象。
通过接口生成器InterfaceMaker和字节码增强器Enhancer实现动态代理
一、作用:
InterfaceMaker会动态生成一个接口,该接口包含指定类定义的所有方法。
二、示例:
比较简单,先定义一个类,仍使用本系列第一篇中的那个ConcreteClassNoInterface类,该类包含3个方法:
- public class ConcreteClassNoInterface {
- public String getConcreteMethodA(String str){
- System.out.println("ConcreteMethod A ... "+str);
- return str;
- }
- public int getConcreteMethodB(int n){
- System.out.println("ConcreteMethod B ... "+n);
- return n+10;
- }
- public int getConcreteMethodFixedValue(int n){
- System.out.println("getConcreteMethodFixedValue..."+n);
- return n+10;
- }
- }
用这个类内定义的方法来生成一个接口:
- InterfaceMaker im=new InterfaceMaker();
- im.add(ConcreteClassNoInterface.class);
- Class interfaceOjb=im.create();
- System.out.println(interfaceOjb.isInterface());//true
- System.out.println(interfaceOjb.getName());//net.sf.cglib.empty.Object$$InterfaceMakerByCGLIB$$13e205f
interfaceOjb就是InterfaceMaker生成的接口,从接口名字可以看出。
看一下该接口内部的方法:
- Method[] methods = interfaceOjb.getMethods();
- for(Method method:methods){
- System.out.println(method.getName());
- }
输出结果,与ConcreteClassNoInterface类内定义的方法完全相同:
- getConcreteMethodA
- getConcreteMethodB
- getConcreteMethodFixedValue
下面通过生成的接口,可以对某个类进行Enhancer(本系列前面介绍过Enhancer,此处不再讲解)。
- Object obj = Enhancer.create(Object.class, new Class[]{ interfaceOjb },
- new MethodInterceptor() {
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- return "intercept!";
- }
- });
- Method method = obj.getClass().getMethod("getConcreteMethodA", new Class[]{String.class});
- System.out.println(method.invoke(obj, new Object[]{"12345"}));
此处让Object生成的代理类实现了由InterfaceMaker生成的接口,但是由于Object类并没有覆写其中的方法,因此,每当对生成接口内方法进行MethodInterceptor方法拦截时,都返回一个字符串,并在最后打印出来。
Cglib代码生成库
CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。其底层是通过小而快的字节码处理框架ASM(http://forge.ow2.org/projects/asm,使用BSD License)来转换字节码并生成新的类。大部分功能实际上是asm所提供的,CGlib只是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。
CGlib被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截);最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包,它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的基本代码很少,但学起来有一定的困难,主要是缺少文档,API描述过于简单,这也是开源软件的一个不足之处。目前CGLIB的版本是cglib-2.2.jar,主要由一下部分组成:
(1)net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。
(2)net.sf.cglib.transform:编译期或运行期类和类文件的转换。
(3)net.sf.cglib.proxy :实现创建代理和方法拦截器的类。
(4)net.sf.cglib.reflect :实现快速反射和C#风格代理的类。
(5)net.sf.cglib.util:集合排序工具类。
(6)net.sf.cglib.beans:JavaBean相关的工具类。
CGLIB包是在ASM之上的一个高级别的层。对代理那些没有实现接口的类非常有用。本质上,它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors),这比JDK动态代理方法快多了。可见,Cglib的原理是对指定的目标类动态生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类和final方法进行代理。
用Cglib创建动态代理
下图表示Cglib常用到的几类。
图1 Cglib主要的接口
创建一个具体类的代理时,通常要用到的CGLIB包的APIs:
net.sf.cglib.proxy.Callback接口:在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。
net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回调(callback)类型,它经常被AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法。
[java] view plain copy
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
当net.sf.cglib.proxy.MethodInterceptor做为所有代理方法的回调 (callback)时,当对基于代理的方法调用时,在调用原对象的方法的之前会调用这个方法,如图下图所示。第一个参数是代理对像,第二和第三个参数分别 是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。
图1
net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要,当对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型。例如:
net.sf.cglib.proxy.FixedValue:为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
net.sf.cglib.proxy.NoOp:NoOp回调把对方法调用直接委派到这个方法在父类中的实现。
net.sf.cglib.proxy.LazyLoader:当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
net.sf.cglib.proxy.Dispatcher:Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。
代理类的所以方法经常会用到回调(callback),当然你也可以使用net.sf.cglib.proxy.CallbackFilter 有选择的对一些方法使用回调(callback),这种考虑周详的控制特性在JDK的动态代理中是没有的。在JDK代理中,对 java.lang.reflect.InvocationHandler方法的调用对代理类的所有方法都有效。
CGLIB的代理包也对net.sf.cglib.proxy.Mixin提供支持。基本上,它允许多个对象被绑定到一个单一的大对象。在代理中对方法的调用委托到下面相应的对象中。
接下来我们看看如何使 用CGLIB代理APIs创建代理。
1、创建一个简单的代理
CGLIB代理最核心类net.sf.cglib.proxy.Enhancer, 为了创建一个代理,最起码你要用到这个类。首先,让我们使用NoOp回调创建一个代理。
[java] view plain copy
/**
* Create a proxy using NoOp callback. The target class
* must have a default zero-argument constructor
*
* @param targetClass the super class of the proxy
* @return a new proxy for a target class instance
*/
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(NoOp.INSTANCE);
return enhancer.create();
}
返回值是target类一个实例的代理。在这个例子中,我们为net.sf.cglib.proxy.Enhancer 配置了一个单一的回调(callback)。我们可以看到很少直接创建一个简单的代理,而是创建一个net.sf.cglib.proxy.Enhancer的实例,在net.sf.cglib.proxy.Enhancer类中你可使用静态帮助方法创建一个简单的代理。一般推荐使用上面例子的方法创建代理,因为它允许你通过配置net.sf.cglib.proxy.Enhancer实例很好的控制代理的创建。
要注意的是,target类是作为产生的代理的父类传进来的。不同于JDK的动态代理,它不能在创建代理时传target对象,target对象必须被CGLIB包来创建。在这个例子中,默认的无参数构造器时用来创建target实例的。如果你想用CGLIB来创建有参数的实例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一个参数定义了参数的类型,第 二个是参数的值。在参数中,基本类型应被转化成类的类型。
2、使用MethodInterceptor创建一个代理
为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:
[java] view plain copy
package com.lizjason.cglibproxy;
import java.lang.reflect.Method;
/**
* A simple authorization service for illustration purpose.
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public interface AuthorizationService {
void authorize(Method method);
}
对net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:
[java] view plain copy
package com.lizjason.cglibproxy.impl;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.lizjason.cglibproxy.AuthorizationService;
/**
* A simple MethodInterceptor implementation to
* apply authorization checks for proxy method calls.
*/
public class AuthorizationInterceptor implements MethodInterceptor {
private AuthorizationService authorizationService;
/**
* Create a AuthorizationInterceptor with the given AuthorizationService
*/
public AuthorizationInterceptor (AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
/**
* Intercept the proxy method invocations to inject authorization check. * The original
* method is invoked through MethodProxy.
*/
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (authorizationService != null) {
//may throw an AuthorizationException if authorization failed
authorizationService.authorize(method);
}
return methodProxy.invokeSuper(object, args);
}
}
我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象。
下面是一个完整的使用MethodInterceptor的例子。
[java] view plain copy
package cglibexample;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 定义一个HelloWorld类,没有实现接口
*
*/
class HelloWorld {
public void sayHelloWorld() {
System.out.println("HelloWorld!");
}
}
/**
* 通过Cglib实现在方法调用前后向控制台输出两句字符串
*
*/
class CglibProxy implements MethodInterceptor {
//要代理的原始对象
private Object obj;
public Object createProxy(Object target) {
this.obj = target;
Enhancer enhancer = new Enhancer();
// 设置要代理的目标类,以扩展它的功能
enhancer.setSuperclass(this.obj.getClass());
// 设置单一回调对象,在回调中拦截对目标方法的调用
enhancer.setCallback(this);
//设置类装载器
enhancer.setClassLoader(target.getClass().getClassLoader());
//创建代理对象
return enhancer.create();
}
/**
* 回调方法:在代理实例上拦截并处理目标方法的调用,返回结果
*
* @param proxy 代理类
* @param method 被代理的方法
* @param params 该方法的参数数组
* @param methodProxy
*/
@Override
public Object intercept(Object proxy, Method method, Object[] params,
MethodProxy methodProxy) throws Throwable {
Object result = null;
// 调用之前
doBefore();
// 调用目标方法,用methodProxy,
// 而不是原始的method,以提高性能
result = methodProxy.invokeSuper(proxy, params);
// 调用之后
doAfter();
return result;
}
private void doBefore() {
System.out.println("before method invoke");
}
private void doAfter() {
System.out.println("after method invoke");
}
}
public class TestCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
HelloWorld hw = (HelloWorld) cglibProxy.createProxy(new HelloWorld());
hw.sayHelloWorld();
}
}
输出结果:
[plain] view plain copy
before method invoke
HelloWorld!
after method invoke
基本流程:需要自己写代理类,它实现MethodInterceptor接口,有一个intercept()回调方法用于拦截对目标方法的调用,里面使用methodProxy来调用目标方法。创建代理对象要用Enhance类,用它设置好代理的目标类、有intercept()回调的代理类实例、最后用create()创建并返回代理实例。
3、使用CallbackFilter在方法层设置回调
net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:save和load,其中方法save需要权限检查,而方法load不需要权限检查。
[java] view plain copy
import com.lizjason.cglibproxy.PersistenceService;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
/**
* A simple implementation of PersistenceService interface
*/
class PersistenceServiceImpl implements PersistenceService {
//需要权限检查
public void save(long id, String data) {
System.out.println(data + " has been saved successfully.");
}
//不需要权限检查
public String load(long id) {
return "Test CGLIB CallBackFilter";
}
}
/**
* An implementation of CallbackFilter for PersistenceServiceImpl
*/
public class PersistenceServiceCallbackFilter implements CallbackFilter {
//callback index for save method
private static final int SAVE = 0;
//callback index for load method
private static final int LOAD = 1;
/**
* Specify which callback to use for the method being invoked.
* @param method the method being invoked.
* @return
*/
@Override
public int accept(Method method) {
//指定各方法的代理回调索引
String name = method.getName();
if ("save".equals(name)) {
return SAVE;
}
// for other methods, including the load method, use the
// second callback
return LOAD;
}
}
accept方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。
[java] view plain copy
...
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersistenceServiceImpl.class);
//设置回调过滤器
CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();
enhancer.setCallbackFilter(callbackFilter);
//创建各个目标方法的代理回调
AuthorizationService authorizationService = ...
Callback saveCallback = new AuthorizationInterceptor(authorizationService);
Callback loadCallback = NoOp.INSTANCE;
//顺序要与指定的回调索引一致
Callback[] callbacks = new Callback[]{saveCallback, loadCallback };
enhancer.setCallbacks(callbacks); //设置回调
...
return (PersistenceServiceImpl)enhancer.create(); //创建代理对象
在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])方法指定代理对象所实现的接口。
除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。
4、使用Mixin
Mixin通过代理方式将多种类型的对象绑定到一个大对象上,这样对各个目标类型中的方法调用可以直接在这个大对象上进行。下面是一个例子。
[java] view plain copy
import net.sf.cglib.proxy.Mixin;
interface MyInterfaceA {
public void methodA();
}
interface MyInterfaceB {
public void methodB();
}
class MyInterfaceAImpl implements MyInterfaceA {
@Override
public void methodA() {
System.out.println("MyInterfaceAImpl.methodA()");
}
}
class MyInterfaceBImpl implements MyInterfaceB {
@Override
public void methodB() {
System.out.println("MyInterfaceBImpl.methodB()");
}
}
public class Main {
public static void main(String[] args) {
//各个对象对应的类型
Class[] interfaces = new Class[]{MyInterfaceA.class, MyInterfaceB.class};
//各个对象
Object[] delegates = new Object[]{new MyInterfaceAImpl(), new MyInterfaceBImpl()};
//将多个对象绑定到一个大对象上
Object obj = Mixin.create(interfaces, delegates);
//直接在大对象上调用各个目标方法
((MyInterfaceA)obj).methodA();
((MyInterfaceB)obj).methodB();
}
}
动态生成Bean
我们知道,Java Bean包含一组属性字段,用这些属性来存储和获取值。通过指定一组属性名和属性值的类型,我们可以使用Cglib的BeanGenerator和BeanMap来动态生成Bean。下面是一个例子。
[java] view plain copy
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
/**
* 动态实体bean
*
* @author cuiran
* @version 1.0
*/
class CglibBean {
//Bean实体Object
public Object object = null;
//属性map
public BeanMap beanMap = null;
public CglibBean() {
super();
}
@SuppressWarnings("unchecked")
public CglibBean(Map<String, Class> propertyMap) {
//用一组属性生成实体Bean
this.object = generateBean(propertyMap);
//用实体Bean创建BeanMap,以便可以设置和获取Bean属性的值
this.beanMap = BeanMap.create(this.object);
}
/**
* 给bean中的属性赋值
*
* @param property 属性名
* @param value 值
*/
public void setValue(String property, Object value) {
beanMap.put(property, value);
}
/**
* 获取bean中属性的值
*
* @param property 属性名
* @return 值
*/
public Object getValue(String property) {
return beanMap.get(property);
}
/**
* 得到该实体bean对象
*
* @return
*/
public Object getObject() {
return this.object;
}
@SuppressWarnings("unchecked")
private Object generateBean(Map<String, Class> propertyMap) {
//根据一组属性名和属性值的类型,动态创建Bean对象
BeanGenerator generator = new BeanGenerator();
Set keySet = propertyMap.keySet();
for (Iterator i = keySet.iterator(); i.hasNext();) {
String key = (String) i.next();
generator.addProperty(key, (Class) propertyMap.get(key));
}
return generator.create(); //创建Bean
}
}
/**
* Cglib测试类
*
* @author cuiran
* @version 1.0
*/
public class CglibTest {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws ClassNotFoundException { // 设置类成员属性
HashMap<String, Class> propertyMap = new HashMap<>();
propertyMap.put("id", Class.forName("java.lang.Integer"));
propertyMap.put("name", Class.forName("java.lang.String"));
propertyMap.put("address", Class.forName("java.lang.String")); // 生成动态Bean
CglibBean bean = new CglibBean(propertyMap);
// 给Bean设置值
bean.setValue("id", 123); //Auto-boxing
bean.setValue("name", "454");
bean.setValue("address", "789");
// 从Bean中获取值,当然获得值的类型是Object
System.out.println(" >> id = " + bean.getValue("id"));
System.out.println(" >> name = " + bean.getValue("name"));
System.out.println(" >> address = " + bean.getValue("address"));
// 获得bean的实体
Object object = bean.getObject();
// 通过反射查看所有方法名
Class clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method curMethod : methods) {
System.out.println(curMethod.getName());
}
}
}
输出结果:
[java] view plain copy
>> id = 123
>> name = 454
>> address = 789
getAddress
getName
getId
setName
setId
setAddress
CGLIB轻松实现延迟加载
通过使用LazyLoader,可以实现延迟加载,即在没有访问对象的字段或方法之前并不加载对象,只有当要访问对象的字段或方法时才进行加载。下面是一个例子。
[java] view plain copy
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.LazyLoader;
class TestBean {
private String userName;
/**
* @return the userName
*/
public String getUserName() {
return userName;
}
/**
* @param userName the userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}
}
//延迟加载代理类
class LazyProxy implements LazyLoader {
//拦截Bean的加载,本方法会延迟处理
@Override
public Object loadObject() throws Exception {
System.out.println("开始延迟加载!");
TestBean bean = new TestBean(); //创建实体Bean
bean.setUserName("test"); //给一个属性赋值
return bean; //返回Bean
}
}
public class BeanTest {
public static void main(String[] args) {
//创建Bean类型的延迟加载代理实例
TestBean bean = (TestBean) Enhancer.create(TestBean.class, new LazyProxy());
System.out.println("------");
System.out.println(bean.getUserName());
}
}
输出结果:
[java] view plain copy
------
开始延迟加载!
test
我们创建TestBean类的延迟代理,通过LazyLoader中的loadObject()方法的拦截,实现对TestBean类的对象进行延迟加载。从输出可以看出,当创建延迟代理时,并没有立刻加载目标对象(因为还有输出“开始延迟加载!”),当通过代理访问目标对象的getUserName()方法时,就会加载目标对象。可见loadObject()是延迟执行的。
参考文献:
Java动态代理一——动态类Proxy的使用 - Ruthless - 博客园
http://wenku.baidu.com/view/3f92297c27284b73f24250b9.html
-
- (Decorator)(eg:IO流之流嵌套)
含义:
•装饰者模式(Decorator Pattern)又称包装模式(Wrapper Pattern)。
•装饰模式以对客户端透明的方式动态扩展对象的功能,是继承关系静态扩展类的功能的一个替代方案。
•装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。
•装饰模式把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展完全是透明的。
•装饰模式通过创建一个包装对象,也就是装饰者来包裹真实对象,以扩展真实对象的功能。
装饰模式的角色:
–抽象构件角色(Component):给出一个抽象接口(抽象父类),以规范准备接收附加责任的对象。(例如InputStream)
–具体构件角色(Concrete Component):定义一个将要接收附加责任的类(继承或实现抽象构件接口或父类)。(例如FileInputStream)
–装饰角色(Decorator):持有一个构件(Component)对象的引用,并实现(继承)抽象构件接口(抽象父类)。(例如FilterInputStream)
–具体装饰角色(Concrete Decorator):与具体构件角色有相同的接口,负责给构件对象“贴上”附加责任。(例如BufferedInputStream)
InputStream inputStream = new BufferedInputStream(new FileInputStream("E:/Demo.txt")); |
装饰模式的特点
–装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
–装饰对象包含一个真实对象的引用(reference)
–装饰对象接收所有来自客户端的请求。它把这些请求转发给真实的对象。
–装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
装饰者模式VS继承VS动态代理模式
装饰者模式 | 继承 | 动态代理模式 |
用来扩展特定对象的功能 | 用来扩展一类对象的功能 | |
不需要子类 | 需要子类 | |
动态 | 静态 | |
运行时分配职责 | 编译时分派职责 | |
防止由于子类而导致的复杂和混乱 | 导致很多子类产生 | |
更多的灵活性 | 缺乏灵活性 |
–对于一个给定的对象,同时可能有不同的装饰对象,客户端可以通过它的需要选择合适的装饰对象发送消息。
代理模式和装饰模式非常类似,甚至代码都类似。二者最主要的区别是:
1)代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行。而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能。
2)装饰者模式支持多层嵌套,代理模式不支持(?)。
3)装饰角色持有的是抽象构件角色的引用,而代理角色持有的是对真实角色的引用。
编程步骤
1)编写一个装饰类继承被装饰类,被装饰类不能是final的。
2)在装饰类中定义一个成员变量(被装饰类类型),用于接收被装饰类对象。
3)在装饰类的构造方法中传入被装饰类对象,赋值给成员变量。
4)在装饰类类中重写被装饰类方法,添加新功能。
示例
package com.tongwx.io.decorator; public class TestDecorator { public static void main(String[] args) { //装饰者还可以装饰另一装饰者 Decorator decorator = new ConcreteDecorator2(new ConcreteDecorator1(new ConcreteComponent())); decorator.doSomething(); } } /** * 抽象构件角色 */ interface Component { public void doSomething(); } /** * 具体构件角色 */ class ConcreteComponent implements Component {//实现或继承抽象构件角色 @Override public void doSomething() { System.out.println("聚众闹事!"); } } /** * 装饰角色 */ class Decorator implements Component {//实现或继承抽象构件角色 private Component component;//持有抽象构件角色的引用 public Decorator(Component component) {//带抽象构件角色的构造方法 this.component = component; } @Override public void doSomething() { component.doSomething();//调用抽象构件角色的在构造器中传入的实现类对象的同名方法 } } /** * 具体装饰角色1 */ class ConcreteDecorator1 extends Decorator {//继承装饰角色 public ConcreteDecorator1(Component component) { super(component); } @Override public void doSomething() { super.doSomething();//调用父类同名方法,父类调用抽象构件角色的实现类对象的同名方法 doAnotherThing();///增加自己附加的任务 } private void doAnotherThing() { System.out.println("把事情闹大!");//自己附加的的任务 } } /** * 具体装饰角色2 */ class ConcreteDecorator2 extends Decorator {//继承装饰角色 public ConcreteDecorator2(Component component) { super(component); } @Override public void doSomething() { super.doSomething();//调用父类同名方法,父类调用抽象构件角色的实现类对象的同名方法 doAnotherThing();///增加自己附加的任务 } private void doAnotherThing() { System.out.println("按闹分配!");//自己附加的的任务 } } |
-
- (eg:Spring之IOC工厂)
封装创建对象时的初始化工作
工厂模式是用来创建实例对象的,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
如果创建对象时初始化工作不仅是赋值这样简单的事,而是很长一段代码(如连接数据库),说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装 ”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从构造函数中分离出去,包装成工厂方法。
创建不同类型的子类或实现类
你想如果有多个类似的类,我们就需要实例化出来多个类。这样代码管理起来就太复杂了,这个时候你就可以采用工厂方法来封装这个问题。不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample
- Sample mysample=new MySample();
- Sample hissample=new HisSample();
采用工厂封装:
- public class Factory{
- public static Sample creator(int which){
- //getClass 产生Sample 一般可使用动态类装载装入类。
- if (which==1)
- return new SampleA();
- else if (which==2)
- return new SampleB();
- }
- }
那么在你的程序中,如果要实例化Sample时.就使用
- Sample sampleA=Factory.creator(1);
举个更实际的例子,比如你写了个应用,里面用到了数据库的封装,你的应用可以今后需要在不同的数据库环境下运行,可能是oracle,db2,sql server等,那么连接数据库的代码是不一样的,你用传统的方法,就不得不进行代码修改来适应不同的环境,非常麻烦,但是如果你采用工厂类的话,将各种可能的数据库连接全部实现在工厂类里面,通过你配置文件的修改来达到连接的是不同的数据库,那么你今后做迁移的时候代码就不用进行修改了。
我通常都是用xml的配置文件配置许多类型的数据库连接,非常的方便。PS:工厂模式在这方面的使用较多。
模板模式():
编程步骤:
1. 先写出解决该类事务其中一件的方案。
2. 分析代码,把会发生变化的代码抽取出来独立成一个抽象方法。
3. 使用final修饰模板方法,防止别人重写你的模板方法。
/*需求;编写一个计算程序运行时间 的模板。*/ abstract class MyRuntime { public final void getTime() { long startTime = System.currentTimeMillis();// 记录开始的时间 code(); long endTime = System.currentTimeMillis();// 记录结束的时间 System.out.println("运行时间 :" + (endTime - startTime)); } public abstract void code(); } | public class Demo extends MyRuntime { public static void main(String[] args) { Demo d = new Demo(); d.getTime(); } // code方法内部就写要计算运行时间 的代码; public void code() { int i = 0; while (i < 100) { System.out.println("i=" + i); i++; } } } |