设计模式
六大原则
单一职责
类和方法的职责单一,不要把不同的业务逻辑放在同一个类或一个方法里面
迪米特法则
一个类尽可能降低对其他类的依赖、这样它的可维护性和可扩展性更高
接口隔离原则
设计接口的时候要尽可能的精简单一,内部逻辑尽量少对外暴露
里氏替换
不要破坏它的继承体系,当你去扩展父类的时候,不要去覆盖父类的非抽象方法,如果覆盖了,别人看代码的时候就会比较不好看,不容易判断调的方法时子类的还是父类的。
依赖倒置
面向接口编程,而不是直接调用具体的实现类。就指需要依赖的时候,就要依赖接口,而不是依赖具体的类
开闭原则
对扩展开放,对修改关闭
工厂模式
简单工厂模式
白话解说
就是建一个工厂类(这个工厂是将生产产品的过程给封装起来),然后调用者只需要告诉工厂类你需要一个什么样的产品,工厂类就会返回一个对应的产品(另外,因为这个产品又跟抽象产品类构成一个多态关系,这样的话工厂类中返回的无论是什么产品都可以使用这个抽象产品类进行接收),随后返回进行处理
UML类图
工厂方法模式
白话解说
在简单工厂模式的基础上,将工厂类抽象化,并建立具体工厂子类使其具体生产对应的产品。
举个例子
简单模式就是你现在要做一个产品A,然后我(具体生产产品的工厂)里面有员工A能够生产产品A,然后你要产品A,我就找员工A给你做。这个员工A就是属于我的内部资源。
那如果你现在要一个产品B,但是我工厂里面没有对应的员工B会生产这个产品B,那我就得去招一个会做产品B的员工,那从我工厂内部来说,我的工厂就增加了一个员工(形成了代码的修改)
工厂方法模式就是你告诉我(总工厂、不做事,就是安排哪个工厂去生产产品的工厂,即抽象工厂)你要一个产品A,那我就建一个专门建产品A的工厂去帮你生产这个产品A,那如果你想要产品B,那这个时候我就再建一个专门建产品B的工厂去专门生产产品B,这样的话我就不用修改我(总工厂,即抽象工厂)内部的资源,就满足了所谓的开闭原则,即对扩展开放,对修改关闭。这时候每个产品创建工厂与每个产品有关系,两个抽象类之间的关系,仅在创建的产品返回值之上
UML类图
抽象工厂模式
白话解说
抽象工厂就是说在上面抽象产品工厂之上,再进行抽象一层。
举个例子,比如我现在有一个工厂抽象类,然后工厂下面又有一个造飞机的工厂类、一个造汽车的工厂类。然后造飞机的工厂类下面又有具体造波音747的,马航的。造汽车的工厂类下面有造宝马的、奔驰的。
策略模式
标准定义定义一组算法,将每一个算法封装起来,从而使它们可以相互切换
特点
1)一组算法,就是不同的策略
2)这组算法都实现了相同的接口或者继承相同的抽象类,所以可以相互切换
白话解说
策略模式就是说假如一道题,我现在有解法接口及三个具体实现分别为方式一、方式二、方式三,然后我现在有一个类聚合了一个解法接口对象,那这样的话,我就可以传三个具体的实现让这个Context类的属性进行一个接收。那我在创建这个Context对象的时候,传递一个具体的解法,然后我初始化完之后就可以调用这个接口的方法,那这个方法的接口又被对应的方式一、二、三进行了实现,那这样我就可以调用到想要的方法了。
UML类图
优缺点
优点
(1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了⼀个算法或⾏为族。恰当使⽤继承可以把公共的代码移到⽗类⾥⾯,从⽽避免代码重复。
(2)使⽤策略模式可以避免使⽤多重条件(if-else)语句。多重条件语句不易维护,它把采取哪⼀种算 法或采取哪⼀种⾏为的逻辑与算法或⾏为的逻辑混合在⼀起,统统列在⼀个多重条件语句⾥⾯,⽐使⽤继承的办法还要原始和落后。
缺点
(1)客户端必须知道所有的策略类,并⾃⾏决定使⽤哪⼀个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换⾔之,策略模式只适⽤于客户端知道算法或⾏为的情况。
(2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数⽬就会很可观。
单例设计模式
概念单例对象的类必须保证只有一个实例存在
白话解说
单例模式呢,从名字来说,”单例“,单就是单一的一个的意思,例就是类的一个实例的意思,也也就是我们常说的对象。那总的意思就是说,这个类,只能创建一个对象。那么怎么判断这个类只创建了一个对象呢,其实就是使用我们的==符号,因为我们这个符号比较的是就是两个对象指向的是否是同一个地址,那如果某个类的所有对象的地址值都相同,那不就说明它是一个同一个对象了嘛。
那我们应该怎么做才能实现这个单例呢?我们试想,我们平时创建一个对象的时候,都是通过new关键字,那为什么new关键字在任意地方都可以创建某个类的对象呢?这是因为我们一般类的构造方法他都是public的,public所修饰的不管是方法还是属性都是随处可以调用的,当然构造方法也是一样的。我们的对象加载经过类加载、验证、准备、解析、初始化五个阶段才能new出来,new出来的对象是存在于堆内存之中,每个对象的地址不同,就此而已,显然我们的对象就肯定不是唯一的。那我们为了使对象唯一,那我们将构造方法的权限修饰符修改为private(为什么不是使用protected与默认修饰符的原因同上),这样的话,我们就不能从外部new出来。这样就不会随意的被创建。
那我们再想一下,我们将我们的构造方法进行私有化之后,那除了本类之外的任何地方都不能进行调用了,那我们应该如何获取我们的类实例呢?我们的一个类中包含属性与方法,方法中构造方法被私有化,那我们试想一下能否使用方法来返回这个实例呢?答案显然是可以的。那我们的这个方法修饰符应该用public(因为是为了要让外部地方来调用此方法),方法名的话用getInstance(见名知意,获得一个实例),返回值类型用本类类名。这样就可以了嘛?我们在想,这是一个普通方法,一般来说我们调用一个方法时必须通过对象来进行调用的(反射除外),那问题是我们的构造方法被我们私有化了,没办法创建对象。那么问题来了,如何不通过对象来调用方法呢?很容易我们就能够想到static关键字,通过类名.来调用该方法,那我们调用这个方法是用来做什么的呢?用来获取实例的,那最简单的方式就是直接在方法内返回一个实例,返回这个实例又不能使用new关键字,因为如果使用new关键字的话,就又会出现上面说的对象不唯一的问题。
那我们就继续想,一个类中除了方法是还有属性的。那这个属性的修饰符应该是怎样的呢。首先该变量应该是private的是也是为了防止被调用,那我们就是在这个属性上进行new的一个操作,那设想如果这个修饰符不是private的话,那她如果被别人进行调用了,是不是就又被new了,同样的问题就又出现了。那我们是不是只需要加private的话就够了呢?我们想一个类被加载是只被加载一次,但是类的对象是不是创建一个,属性就多一个备份呢?对吧,那我们如何避免这个情况呢?static就来了,static确保他只被加载一次。
我们以上所描述的类就是著名的单例饿汉式写法。(按以上思路可以快速理解和记忆单例饿汉式写法)
应用场景
单例模式只允许创建⼀个对象,因此节省内存,加快对象访问速度,因此对象需要被公⽤的场合适合使⽤,如多个模块使⽤同⼀个数据源连接对象等等。如:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但⼜经常⽤到的对象。
- 有状态的⼯具类对象。
- 频繁访问数据库或⽂件的对象。
有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数 据,是⾮线程安全的。在不同⽅法调⽤间不保留任何状态。
⽆状态就是⼀次操作,不能保存数据。⽆状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
饿汉式
单例饿汉式就是你一调用这个getInstance方法,不管三七二十一就直接给你new一个返回给你,显得很着急,很迫不及待,就如一个饿了三天三夜的流浪汉看到食物一般。饿汉天生是线程安全
/**
饿汉式
优点:没有线程安全问题,简单
缺点:提前初始化会延⻓类加载器加载类的时间;如果不使⽤会浪费内存空间; 不能传递参数
*/
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
懒汉式
单例懒汉式就是你一调用这个getInstance方法,就是你调用方法的时候,先问一下,看看原本是否有创建好的instance,就是比较懒,有现成的实例就捡现成的,不愿意再去创建,如果没有才去进行创建。懒汉在多线程下是有问题的:这是因为但线程A调用getInstance方法,走到if(null == instance) 但未继续向下执行时,线程B也来调用getInstance方法,这个时候instance还未被创建,依旧为null,所以线程B也会进入判断,执行代码,从而形成两个对象,此即出现了多线程问题。
/**
* 懒汉式
*/
public class Singleton {
private static Singleton instance = null;//JVM加载字节码文件后,对象引用为null,等调用getInstance()方法才去创建对象
private Singleton(){};
public static Singleton getInstance(){
if(null == instance) {
instance = new Singleton();
}
return instance;
}
}
双重检查(double checked locking)
为什么需要volatile
1.使线程之间具有可见性:使多线程中的变量共享
这个是说,线程A跟线程B都读到一个属性a=0,然后在线程A中,这个a被+1变成了1,这样的话,线程B去执行的时候,读到的属性a就变成1,再去进行+1操作,可变成2
2.防止指令重排序
在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了(本来这个赋值应该是最后一步操作,但由于指令重排序可能提前赋值),然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。
为什么需要检查两次
第一次校验:由于单例模式只需要创建一次实例,如果后面再次调用getInstance方法时,则直接返回之前创建的实例,因此大部分时间不需要执行同步方法里面的代码,大大提高了性能。如果不加第一次校验的话,每次都要去竞争锁。
第二次校验:如果没有第二次校验,假设线程t1执行了第一次校验后,判断为null,这时t2也获取了CPU执行权,也执行了第一次校验,判断也为null。接下来t2获得锁,创建实例。这时t1又获得CPU执行权,由于之前已经进行了第一次校验,结果为null(不会再次判断),获得锁后,直接创建实例。结果就会导致创建多个实例。所以需要在同步代码里面进行第二次校验,如果实例为空,则进行创建。
/**
* 双重检查
*/
public class Singleton {
private Singleton() {}
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
单例模式的破坏与防止
反射在java中是无所不能的,这句话大家应该是有听过吧。那么,这句话也同样适用于此
反射可以破坏单例模式
Singleton sc1 = Singleton.getInstance();
Singleton sc2 = Singleton.getInstance();
// 此处sc1和sc2对象为同一个对象
System.out.println(sc1);
System.out.println(sc2);
// 通过反射的方式调用私有构造方法
Class<Singleton> clazz = (Class<Singleton>) Class.forName(" com.learn.example.Singleton" );
Constructor<Singleton> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true); //
Singleton sc3 = c.newInstance();
Singleton sc4 = c.newInstance();
// 此处sc3与sc4不是同一个对象
System.out.println("通过反射的方式获取的对象sc3");
System.out.println("通过反射的方式获取的对象sc4");
// 在调用构造方法的是,判断通过反射得到的实例是否为空,如果为空则报错
private Singleton() {
if (null != SingletonClassInstance.instance)
throw new RuntimeException();
}
源码中的应用
单例对象的生命周期:与容器同生同亡
多例对象的生命周期:被使用的时候才创建对象,JVM会判断该对象是否长期未被使用,如果没有就会被回收
1.spring中单例
当多个⽤户同时请求⼀个服务时,容器会给每⼀个请求分配⼀个线程,这时多个线程会并发执⾏该请求对应的业务逻辑(成员⽅法),此时,如果该处理逻辑中有对单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。
2.源码实现(供参考)
public abstract class AbstractBeanFactory implements Configurable BeanFactory {
/**
* 充当了Bean 实例的缓存,实现⽅式和单例注册表相同
**/
private final Map singletonCache = new HashMap();
public Object getBean(String name) throws BeansException {
return getBean(name, null, null);
}
public Object getBean(String name, Class requiredType, Object[] args) throws BeansException {
// 对传⼊的Bean name稍做处理,防⽌传⼊的Bean name 名有⾮法字符(或则做转码)
String beanName = transformedBeanName(name);
Object bean = null;
// ⼿⼯检测单例注册表
Object sharedInstance = null;
// 使⽤了代码锁定同步块,原理和同步⽅法相似,但是这种写法效率更⾼
synchronized (this.singletonCache) {
sharedInstance = this.singletonCache.get(beanName);
}
// 如果sharedInstance不为空那么就返回已有的对象
if (sharedInstance != null) {
...
// 返回合适的缓存Bean 实例
bean = getObjectForSharedInstance(name, sharedInstance);
} else {
...
// 取得Bean 的定义
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
...
// 根据Bean定义判断,此判断依据通常来⾃于组件配置⽂件的单例属性开关
//<bean id = "date" class="java.util.Date" scope="singleton" />
// 如果是单例,做如下处理
if (mergedBeanDefinition.isSingleton()) {
synchronized (this.singletonCache) {
// 再次检测单例注册表
sharedInstance = this.singletonCache.get(beanName);
if (sharedInstance == null) {
...
try {
// 真正创建Bean 实例
sharedInstance = createBean(beanName, mergedBeanDefinition, args);
// 向单例注册表注册Bean实例
addSingleton(beanName, sharedInstance);
} catch (Exception ex) {
...
} finally {
...
}
}
}
bean = getObjectForSharedInstance(name, sharedInstance);
// 如果是⾮单例,即prototype,每次都要新创建一个Bean实例
// <bean id=" class="java.util.Date" scope="prototpye"/>
} else {
bean = createBean(beanName, mergedBeanDefinition, args);
}
return bean;
}
}
}
命令模式
概念
当需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,使得请求发送者与请求接收者消解耦合
命令模式:就是将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
白话解说
将军发布命令,士兵去执行。
其中有几个角色: 将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker是调用者(将军),Receiver是被调用者(士兵), MyCommand是命令,实现了Command接口,持有接收对象
应用场景
1)有一系列命令,并且这些命令有优先级
2)需要做一下redo,undo操作
UML类图
- Invoker 是调用者角色
- Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
- Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
- ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute
优缺点
优点
降低系统的耦合度。
新的命令可以很容易地加⼊到系统中。
可以⽐较容易地设计⼀个命令队列和宏命令(组合命令)。
可以⽅便地实现对请求的Undo和Redo。
缺点
使⽤命令模式可能会导致某些系统有过多的具体命令类。因为针对每⼀个命令都需要设计一个具体的命令类。因此某些系统可能需要大量具体命令类,这将影响命令模式的使用
代理模式
代理模式示意图如下:为其他对象提供一种代理。以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式分三种:静态代理、动态代理、Cglib代理
代理模式一般涉及到的角色有:
1.抽象角色:声明真实对象和代理对象的共同接口
2.代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
3.真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类(目的是形成多态关系)
作用:为其他对象提供一种代理以控制对这个对象的访问
简单来说,就比如被代理对象有一个方法需要执行,但是客户需要在执行这个方法的前后要进行一个日志的打印,那我们就可以用一个代理类,去聚合被代理对象,然后再代理类中的方法中调用被代理对象的方法,并且在调用被代理对象的方法的代码上下可以添加我们所要的处理,即此时为日志打印
UML类图
优缺点
优点
可以在不修改目标对象的功能前提下,对目标功能扩展
缺点
每⼀个代理类都必须实现⼀遍委托类(也就是realsubject)的接⼝,如果接⼝增加⽅法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理就会非常臃肿。
jdk动态代理
动态代理解决静态代理中代理类接⼝过多的问题,通过反射来实现的,借助Java⾃带的java.lang.reflect.Proxy通过固定规则生成
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
JDK中生成代理对象的API
- 代理类所在包:java.lang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
/**
* 1.ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
* 2.Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方式确认类型
* 3.InvocationHandler h:事件处理,处理目标对象的方法时,会触发事件处理器的方法,会把当前执行* 的目标对象方法作为参数传入
* /
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
动态代理也叫做:JDK代理、接口代理
应用场景
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
UML类图
原理分析
以下原理分析暂未理解,先行暂存
public class Proxy implements java.io.Serializable {
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{
Objects.requireNonNull(h);
// 准备一份所有被实现的业务接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 代理类生成的核心代码
* classLoader的作用是将字节码文件加载进虚拟机,并生成相应的class
* interfaces就是被实现的那些接口
* h就是InvocationHandler
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new java.lang.reflect.Proxy.KeyFactory(), new java.lang.reflect.Proxy.ProxyClassFactory());
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 代理接口书进行限制
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
// 如果由给定的加载器实现给定的接口的代理类存在,返回缓存,否则,它将通过ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
// 通过ClassLoader和接口列表,生成和定义一个proxy class
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
// 所有代理类的命名前缀
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
// 一个唯一的number作为proxy class的名称标识
private static final AtomicLong nextUniqueNumber = new AtomicLong();
// proxy class生成方法
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
// 确定类加载器解析了这个名字的接口到相同的Class对象
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
// 确认代理的Class是接口,从这可看出JDK动态代理的劣势
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
// 接口重复校验
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in 定义proxy class 所在的包
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
// 记录所有non-public的proxy interfaces都在同一个package中
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
// 如果没有non-public的interfaces,默认包为com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
// 最终大概名字为:com.sun.proxy.$Proxy1
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
// 生成特殊的proxy class
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
}
public class ProxyGenerator {
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
// 私有构造器
private ProxyGenerator(String var1, Class<?>[] var2, int var3) {
this.className = var1;
this.interfaces = var2;
this.accessFlags = var3;
}
private byte[] generateClassFile() {
// 添加从Object基类中继承的方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
// 添加接口中的方法实现
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
// 构造方法
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
sun.misc.ProxyGenerator.ProxyMethod var16 = (sun.misc.ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new sun.misc.ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
// 生成文件
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
sun.misc.ProxyGenerator.FieldInfo var20 = (sun.misc.ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
sun.misc.ProxyGenerator.MethodInfo var21 = (sun.misc.ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
// 返回字节流
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
}
package com.learn.test;
import com.proxy.api.PeopleService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
// 生成的proxy.class
// 生成的类继承了Proxy,实现了要代理的接口PeopleService
public final class $Proxy11 extends Proxy implements PeopleService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public $Proxy11(InvocationHandler paramInvocationHandler) {
// 调用基类Proxy的构造器
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void mainJiu() {
try {
this.h.invoke(this, m3,null);
return ;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2,null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void printName(String paramObject) {
try {
this.h.invoke(this, m4, new Object[] {paramObject});
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode(String paramObject) {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
// 利用反射生成5个方法,包括Object中的equals、toString、hashCode以及PeopleService中的mainJiu和printName
m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] {Class.forName("java.lang.Object")});
m3 = Class.forName("com.proxy.api.PeopleService").getMethod("mainJiu",new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]);
m4 = Class.forName("com.proxy.api.PeopleService").getMethod("printName",new Class[]{Class.forName("java.lang.String")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]);
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
现在看生成的¥Proxy.class反编译的java代码中,调用mainJiu时会this.h.invoke(this, m3, null);调用Proxy类中InvocationHandler的invoke方法
newProxyInstance()通过反射生成含有接口方法的proxy class(继承了Proxy类,实现了需要代理的接口)
因此对该对象的所有方法调用都会转发InvocationHandler.invoke()方法
Cglib动态代理
- 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现 代理-这就是Cglib代理
- Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接 口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
- 在AOP编程中如何选择代理模式:
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
- Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
UML类图
代码实现
1.实现MethodInterceptor方法调用会被转发到该类的intercept()方法
2.在需要使用到target的时候,通过CGLIB动态代理获取代理对象,即如下的getProxyInstance所示
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 返回一个代理对象:是target对象的代理对象
public Object getProxyInstance() {
//1.创建一个工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即代理对象
return enhancer.create();
}
//重写intercept方法,会调用目标对象的方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理模式开始");
Object returnVal = method.invoke(target, objects);
System.out.println("cglib代理模式提交");
return returnVal;
}
}
通过Cglib的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept方法里我们可以加入任何逻辑,比如修改方法参数、加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper方法,我们将调用转发给原始对象。
对于从Object中继承的方法,Cglib代理也会进行代理,如hashCode()、equals()、toString()等,但是getClass()、wait()等方法不会,因为它是final方法,Cglib无法代理
原理分析
CGLIB是⼀个强⼤的⾼性能的代码⽣成包,底层是通过使⽤⼀个⼩⽽快的字节码处理框架ASM,它可以在运⾏期扩展Java类与实现Java接⼝
Enhancer是CGLIB的字节码增强器,可以很⽅便的对类进⾏拓展
创建代理对象的⼏个步骤:
1、⽣成代理类的⼆进制字节码⽂件
2、加载⼆进制字节码,⽣成Class对象( 例如使⽤Class.forName()⽅法 )
3、通过反射机制获得实例构造,并创建代理类对象
总结
1.jdk动态代理:利⽤拦截器(拦截器必须实现InvocationHanlder)加上反射机制⽣成⼀个实现代理接⼝的匿名类,在调⽤具体⽅法前调⽤InvokeHandler来处理。只能对实现了接⼝的类⽣成代理只能对实现了接⼝的类⽣成代理
2. cglib:利⽤ASM开源包,对代理对象类的class⽂件加载进来,通过修改其字节码⽣成⼦类来处理。主要是对指定的类⽣成⼀个⼦类,覆盖其中的⽅法,并覆盖其中⽅法实现增强,但是因为采⽤的是继承,对于final类或⽅法,是⽆法继承的。
3. 选择
a. 如果⽬标对象实现了接⼝,默认情况下会采⽤JDK的动态代理实现AOP。
b. 如果⽬标对象实现了接⼝,可以强制使⽤CGLIB实现AOP。
c. 如果⽬标对象没有实现了接⼝,必须采⽤CGLIB库,Spring会⾃动在JDK动态代理和CGLIB之间转 换。