23种设计模式(一)
1、单例模式
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
//1、
public class Singleton{
private Singleton(){}
private final static Singleton instance=null;
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
}
return instance;
}
/*
这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题
了,如何解决?首先会想到对getInstance方法加synchronized关键字,如下:
*/
//懒汉式
public class Singleton{
private Singleton(){}
private final static Singleton instance;
public static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
}
return instance;
}
/*
但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进
*/
public class Singleton{
private Singleton(){}
private final static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(instance){
if(instance==null){
instance = new Singleton();
}
}
}
}
return instance;
}
public class SingleTest3 {
private static volatile SingleTest6 instance;//一旦修改马上更新
private SingleTest6() {}
//写一个静态内部类,该类中有一个静态属性
private static class SingletonInstance{
private static final SingleTest6 INSTANCE=new SingleTest6();
}
public static synchronized SingleTest6 getInstance(){
return SingletonInstance.INSTANCE;
}
}
//饿汉式
public class HungrySingleton
{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance()
{
return instance;
}
}2、工厂方法模式
/*
工厂方法(FactoryMethod)模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
工厂方法模式的主要优点有:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
其缺点是:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度
在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
在以下的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要
实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
*/
// 2.1普通工厂模式
// 就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
//创建共同接口
public interface Sender{
public void Send();
}
//创建实现类
public class MailSender implements Sender{
@Override
public void Send(){
System.out.println("This is mailsender!");
}
}
public class SmsSender implements Sender{
@Override
public void Send(){
System.out.println("This is SmsSender!");
}
}
//创建工厂类
public class SendFactory{
public Sender produce(String type){
if("mail".equals(type)){
return new MailSender();
}else if("sms".equals(type)){
return new SmsSender();
}else{
System.out.println("请输入正确的类型!");
return null;
}
}
}
//测试
public class FactoryTest{
public static void main(String args){
SendFactory factory = new SendFactory();
Sender sender = factory.produce("sms");
sender.Send();
}
}
输出:this is sms sender!
//2.2多个工厂模式
/* 对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,
则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。*/
//对以上SendFactory 代码作以下修改
public class SendFactory{
public Sender produceMail(){
return new MailSender();
}
public Sender producSms(){
return new SmsSender();
}
}
//2.3静态工厂方法
/*将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。*/
public class SendFactory{
public static Sender produceMail(){
return new MailSender();
}
public static Sender producSms(){
return new SmsSender();
}
}
//2.4抽象工厂方法
/*
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,怎么解决?
就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
*/
//创建一个接口
public interface Sender{
public void Send();
}
//创建实现类
public class MailSender implements Sender{
@Override
public void Send(){
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender{
@Override
public void Send(){
System.out.println("this is mailsender!");
}
}
//提供工厂接口
public interface Provider{
public Sender produce();
}
//创建工厂类
public class SendMailFactory implements Provider{
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce(){
return new SmsSender();
}
}
/*
这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!
*/3.原型模式
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。在Java中,复制对象是通clone()实现的。
创建一个原型类
//这是一个浅复制
public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException{
Prototype proto = (Prototype )super.clone();
return proto;
}
}
/*
很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的.
对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
*/
//写一个深复制的例子
public class Prototype implements Cloneable,Serializable{
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
//深复制
public Object deepClone() throws IOException, ClassNotFoundException{
/*写入当前对象的二进制流*/
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStrean oos = new ObjectOutputStrean(bos);
oos.writeObject(this);
//读出二进制流产生的新对象
ByteArrayInputStream bis = new ByteArrayInputStrea(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
}
class SerializableObject implements Serializable {
private static final long serialVersionUID = 1L;
} 4.建造者模式
建造者(Builder)模式由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成,现在我们来分析其基本结构和实现方法。
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
。
(1) 产品角色:包含多个组成部件的复杂对象
public class Product
{
private String partA;
private String partB;
private String partC;
public void setPartA(String partA)
{
this.partA=partA;
}
public void setPartB(String partB)
{
this.partB=partB;
}
public void setPartC(String partC)
{
this.partC=partC;
}
public void show()
{
//显示产品的特性
}
}
(2) 抽象建造者:包含创建产品各个子部件的抽象方法。
abstract class Builder
{
//创建产品对象
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult()
{
return product;
}
}
(3) 具体建造者:实现了抽象建造者接口。
public class ConcreteBuilder extends Builder
{
public void buildPartA()
{
product.setPartA("建造 PartA");
}
public void buildPartB()
{
product.setPartA("建造 PartB");
}
public void buildPartC()
{
product.setPartA("建造 PartC");
}
}
(4) 指挥者:调用建造者中的方法完成复杂对象的创建。
class Director
{
private Builder builder;
public Director(Builder builder)
{
this.builder=builder;
}
//产品构建与组装方法
public Product construct()
{
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}5.适配器模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
5.1类的适配器模式
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:
public class Source{
public void method1(){
System.out.println("this is original method!");
}
}
public interface Targetable{
/*与原类相同的方法*/
public void method1();
/*新类方法*/
public void method2();
}
//Adapter类继承Source类,实现Targetable接口
public class Adapter extends Source implements Targetable{
@Override
public void method2(){
System.out.println("this is the targetable method");
}
}
//测试类
public class AdapterTest{
public static void main(String[] args){
Targetable target = new Adapter();
target.method1();
target.method2();
}
}输出:
this is original method!
this is the targetable method!
这样Targetable接口的实现类就具有了Source类的功能。
5.2 对象的适配器模式
/*本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。*/
//只需修改Adapter类就可以:
public class Wrapper implements Targetable{
private Source source;
public Wrapper(Source source){
super();
this.source = source;
}
@Override
public void method2(){
System.out.println("this is the targetable method!");
}
@Override
public void method1(){
source.method1();
}
}
public class AdapterTest{
public static void main(String[] args){
Source source = new Source();
Tarable target = new Wrapper(source);
target.method1();
target.method2();
}
}输出与第一种一样,只是适配的方法不同而已。
5.3接口适配器模式
有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
public interface Sourceable{
public void method1();
public void method2();
}
//创建抽象类Wrapper2
public abstract class Wrapper2 implements Sourceable{
public void method1(){}
public void method2(){}
}
//创建实现类
public class SourceSub1 extends Wrapper2{
public void method1(){
System.out.println("the sourceable interface's first Sub1!");
}
}
public class SourceSub2 extends Wrapper2{
public void method2(){
System.out.println("the sourceable interface's second Sub2!");
}
}
总结:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。
6.桥接模式
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
//定义接口
public interface Sourceable{
public void method();
}
//实现类
public class SourceSub1 implements Sourceable{
@Override
public void method(){
System.out.println("this is the first sub!");
}
}
public class SourceSub2 implements Sourceable{
@Override
public void method(){
System.out.println("this is the second sub!");
}
}
//定义一个桥,持有Sourceable的一个实例
public abstract class Bridge{
private Sourceable souce;
public void method(){
source.method();
}
public Sourceable getSource() {
return source;
}
public void setSource(Sourceable source) {
this.source = source;
}
}
public class MyBridge extends Bridge{
public void method(){
getSource().method();
}
}
//测试类
public class BridgeTest {
public static void main(String[] args) {
Bridge bridge = new MyBridge();
/*调用第一个对象*/
Sourceable source1 = new SourceSub1();
bridge.setSource(source1);
bridge.method();
/*调用第二个对象*/
Sourceable source2 = new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
} 7.装饰模式
顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能。
public interface Sourceable{
public void method();
}
public class Source implements Sourceable{
@Override
public void method(){
System.out.println("the original method!");
}
}
public class Decorator implements Sourceable{
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method(){
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
//测试
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
} 输出:
before decorator!
the original method!
after decorator!
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
8、组合模式
组合模式又叫部分-整体模式在处理类似树形结构的问题是比较方便。
public class TreeNode{
private String name;
private TreeNode parent;
private Vector<TreeNode> children = new Vector<TreeNode>();
public TreeNode(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
//添加孩子节点
public void add(TreeNode node){
children.add(node);
}
//删除孩子节点
public void remove(TreeNode node){
children.remove(node);
}
//取得孩子节点
public Enumeration<TreeNode> getChildren(){
return children.elements();
}
}
public class Tree {
TreeNode root = null;
public Tree(String name) {
root = new TreeNode(name);
}
public static void main(String[] args) {
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");
nodeB.add(nodeC);
tree.root.add(nodeB);
System.out.println("build the tree finished!");
}
}
//使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。 9、外观模式
当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。
外观(Facade)模式的定义:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。
1.降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
2.对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
3.降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
外观(Facade)模式的主要缺点如下。
1.不能很好地限制客户使用子系统类。
2.增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
外观(Facade)模式包含以下主要角色。
外观(Facade)角色:为多个子系统对外提供一个共同的接口。
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
客户(Client)角色:通过一个外观角色访问各个子系统的功能。
外观模式的实现代码
public class FacadePattern
{
public static void main(String[] args)
{
Facade f=new Facade();
f.method();
}
}
//外观角色
class Facade
{
private SubSystem01 obj1=new SubSystem01();
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
public void method()
{
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//子系统角色
class SubSystem01
{
public void method1()
{
System.out.println("子系统01的method1()被调用!");
}
}
//子系统角色
class SubSystem02
{
public void method2()
{
System.out.println("子系统02的method2()被调用!");
}
}
//子系统角色
class SubSystem03
{
public void method3()
{
System.out.println("子系统03的method3()被调用!");
}
}10、享元模式
在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。
享元(Flyweight)模式的定义:运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
享元模式中存在以下两种状态:
内部状态,即不会随着环境的改变而改变的可共享部分;
外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。
享元模式的主要角色有如下。
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
享元模式的代码
//非享元角色
public class UnsharedConcreteFlyweight{
private String info;
UnsharedConcreteFlyweight(String info){
this.info=info;
}
public String getInfo(){
return info;
}
public void setInfo(String info){
this.info=info;
}
}
//享元抽象角色
public interface Flyweight{
public void operation(UnsharedConcreteFlyweight state);
}
//具体享元角色
public class ConcreteFlyweight implements Flyweight{
private String key;
ConcreteFlyweight(String key){
this.key=key;
}
System.out.println("具体享元"+key+"被创建!");
}
public void operation(UnsharedConcreteFlyweight outState){
System.out.print("具体享元"+key+"被调用,");
System.out.println("非享元信息是:"+outState.getInfo());
}
//享元工厂角色
public class FlyweightFactory{
private HashMap<String, Flyweight> flyweights=new HashMap<String, Flyweight>();
public Flyeight getFlyweight(String key){
Flyweight flyweight = (Flyweight)flyweights.get(key);
if(flyweight!=null){
System.out.println("具体享元"+key+"已经存在,被成功获取!");
}else{
flyweight=new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
public class FlyweightPattern
{
public static void main(String[] args)
{
FlyweightFactory factory=new FlyweightFactory();
Flyweight f01=factory.getFlyweight("a");
Flyweight f02=factory.getFlyweight("a");
Flyweight f03=factory.getFlyweight("a");
Flyweight f11=factory.getFlyweight("b");
Flyweight f12=factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}程序运行结果如下:
具体享元a被创建!
具体享元a已经存在,被成功获取!
具体享元a已经存在,被成功获取!
具体享元b被创建!
具体享元b已经存在,被成功获取!
具体享元a被调用,非享元信息是:第1次调用a。
具体享元a被调用,非享元信息是:第2次调用a。
具体享元a被调用,非享元信息是:第3次调用a。
具体享元b被调用,非享元信息是:第1次调用b。
具体享元b被调用,非享元信息是:第2次调用b。
11、代理模式
(1).静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
模拟保存动作,定义一个保存动作的接口:IUserDao.java,然后目标对象实现这个接口的方法UserDao.java,此时如果使用静态代理方式,就需要在代理对象(UserDaoProxy.java)中也实现IUserDao接口.调用的时候通过调用代理对象的方法来调用目标对象.
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
//接口IUserDao.java
public interface IuserDao{
void save();
}
//目标对象UserDao.java
public class UserDao implements IUserDao {
pubic void save(){
System.out.println("--以保存数据--");
}
}
//代理对象UserDaoProxy.java
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target = target;
}
public void save(){
System.out.println("开始事物。。。");
target.save();
System.out.println("提交事务。。。");
}
}
//测试类
public class App {
public static void main(String[] args) {
//目标对象
UserDao target = new UserDao();
//代理对象,把目标对象传给代理对象,建立代理关系
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();//执行的是代理的方法
}
}(2).动态代理
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK生成代理对象的API
代理类所在的包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接受三个参数
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方***把当前执行目标对象的方法作为参数传入
接口类IUserDao.java以及接口实现类,目标对象UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类(ProxyFactory.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法
//接口IUserDao.java
public interface IuserDao{
void save();
}
//目标对象UserDao.java
public class UserDao implements IUserDao {
pubic void save(){
System.out.println("--以保存数据--");
}
}
//代理工厂类
public class ProxyFactory{
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//为目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
System.out.println("开始事物2");
Object returnVal=method.invoke(terget,args);
System.out.println("提交事务2");
return returnVal;
}
});
}
}
//测试
public class App {
public static void main(String[] args) {
// 目标对象
IUserDao target = new UserDao();
// 【原始的类型 class cn.itcast.b_dynamic.UserDao】
System.out.println(target.getClass());
// 给目标对象,创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// class $Proxy0 内存中动态生成的代理对象
System.out.println(proxy.getClass());
// 执行方法 【代理对象】
proxy.save();
}
}(3).Cglib代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
代码示例:
//目标对象
public class UserDao{
public void save(){
System.out.println("----已经保存数据!----");
}
}
//Cglib代理工厂
public class ProxyFactory implements MethodInterceptor{
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
//测试类
public class App {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}
