引言:人们在开发过程中会碰到很多个模块使用相同的代码或者对原有模块代码进行拓展,即增强额外的功能操作,根据OCP原则,所以人们开始想办法解决。
下面介绍四种方式:
1、继承
编写一个共同的抽象父类,不同的类分别继承它。
Car.java
abstract public class Car {
public void whoUsed(){
String user = "张三";
System.out.println(user+"使用汽车时间:"+new Date());
}
abstract public void Branch();
}
BMW.java
public class BMW extends Car {
@Override
public void Branch() {
super.whoUsed();
System.out.println("BMW");
}
}
Benci.java
public class Benci extends Car {
@Override
public void Branch() {
super.whoUsed();
System.out.println("Benci");
}
}
ProxyTest.java
System.out.println("-----------父类继承-----------");
BMW bmw = new BMW();
Benci benci = new Benci();
bmw.Branch();
benci.Branch();
运行结果
-----------父类继承-----------
张三使用汽车时间:Thu May 31 16:22:02 CST 2018
BMW
张三使用汽车时间:Thu May 31 16:22:02 CST 2018
Benci
继承的方式,被增强的对象和增强的内容都是固定的,为了让被增强的对象和内容是可变的,我们采用代理模式。
2、静态代理
IPerson.java
public interface IPerson {
public void eat();
public void sleep();
}
Person.java
public class Person implements IPerson {
public void eat() {
System.out.println("吃饭中");
}
public void sleep() {
System.out.println("睡觉中");
}
}
ProxyTest.java
System.out.println("-----------静态代理-----------");
IPerson person = new Person();
StaticPersonProxy proxy = new StaticPersonProxy(person);
proxy.eat();
proxy.sleep();
运行结果
-----------静态代理-----------
吃饭之前:Thu May 31 16:22:02 CST 2018
吃饭中
吃饭之后:Thu May 31 16:22:02 CST 2018
睡觉之前:Thu May 31 16:22:02 CST 2018
睡觉中
睡觉之后:Thu May 31 16:22:02 CST 2018
静态代理使用时,需要定义接口,被代理对象与代理对象一起实现相同的接口。静态代理虽然可以让对象和内容发生改变,但是需要写接口和静态代理类,假设要写50个接口就要写50个静态代理类。为此,程序员经过思考想出了动态代理的方式进行拓展。
3、动态代理
IPerson.java
public interface IPerson {
public void eat();
public void sleep();
}
Person.java
public class Person implements IPerson {
public void eat() {
System.out.println("吃饭中");
}
public void sleep() {
System.out.println("睡觉中");
}
}
DynaProxyFactory.java
public class DynaProxyFactory {
private Object target;
public DynaProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
ClassLoader loader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler handler = (InvocationHandler)new DynaProxyHandle(target);
return Proxy.newProxyInstance(loader,interfaces,handler);
}
}
DynaProxyHandle.java
public class DynaProxyHandle implements InvocationHandler {
private Object target;
public DynaProxyHandle(Object target) {
this.target = target;
}
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("eat")){
System.out.println("吃饭前:"+new Date());
result = method.invoke(target,args);
System.out.println("吃饭后:"+new Date());
}
if (method.getName().equals("sleep")){
System.out.println("睡觉前:"+new Date());
result = method.invoke(target,args);
System.out.println("睡觉后:"+new Date());
}
return result;
}
}
ProxyTest.java
System.out.println("-----------动态代理-----------");
IPerson dynaproxy = (IPerson)new DynaProxyFactory(person).getProxy();
dynaproxy.eat();
dynaproxy.sleep();
运行结果
-----------动态代理-----------
吃饭前:Thu May 31 16:22:02 CST 2018
吃饭中
吃饭后:Thu May 31 16:22:02 CST 2018
睡觉前:Thu May 31 16:22:02 CST 2018
睡觉中
睡觉后:Thu May 31 16:22:02 CST 2018
动态代理使用时,需要定义接口,被代理对象需要实现接口,但代理对象就不需要实现接口。假设要写50个接口就要写1或2个动态代理类(DynaProxyFactory.java、DynaProxyHandle.java),一共51/52个类文件(InvocationHandler可以通过内部匿名类实现)。为了继续减少编写文件个数,为此,程序员经过思考想出了Cglib代理的方式进行拓展。
4、Cglib代理
Apple.java
public class Apple {
public void color(){
System.out.println("red");
}
}
Banana.java
public class Banana {
public void whichSeasonFruit(){
System.out.println("Banana");
}
}
CglibProxyFactory.java
public class CglibProxyFactory implements org.springframework.cglib.proxy.MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
//1.⼯具类
Enhancer en = new Enhancer();
//2.设置⽗类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建⼦类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("张三操作时间:"+new Date());
Object result = method.invoke(target,args);
return result;
}
}
ProxyTest.java
System.out.println("-----------Cglib代理-----------");
Apple apple = new Apple();
Banana banana = new Banana();
Apple appleProxy = (Apple)new CglibProxyFactory(apple).getProxy();
Banana bananaProxy = (Banana)new CglibProxyFactory(banana).getProxy();
appleProxy.color();
bananaProxy.whichSeasonFruit();
运行结果
-----------Cglib代理-----------
张三操作时间:Thu May 31 18:04:50 CST 2018
red
张三操作时间:Thu May 31 18:04:50 CST 2018
Banana
Cglib代理使用时,不需要定义接口,但代理对象和被代理对象都就不需要实现接口。假设无论多少个Bean就要写1代理类,一共1个类文件就可以根据OCP原则拓展原有模块功能。