23种设计模式总结
1 单例模式
单例模式很容易理解,就是每次需要使用一个类对象的时候,我们去拿我们第一次new的对象,而不是每次需要都去new一个,同时我们也需要限制该对象不能直接new出来。单例模式有很多写法,它里面又分为懒汉和饿汉
1.1 最简单的单例模式(饿汉,线程安全,推荐使用)
public class SingletonTest {
private final static SingletonTest instance = new SingletonTest();
/**
* 将构造方法改为私有的,就无法new了
*/
private SingletonTest() {
}
public static SingletonTest getInstance() {
return instance;
}
/**
* 模拟并发情况,100个线程同时获取实例
*
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
SingletonTest instance = SingletonTest.getInstance();
System.out.println(instance.hashCode());
}
}).start();
}
}
}
我们需要person对象的时候,直接使用SingletonTest.getInstance()
就行了
1.2 懒汉模式
上面这个饿汉模式就带来一个问题:有时候我们没有使用到这个对象啊,可是它却在项目启动后就创建了对应类的对象。于是就产生了这个懒汉模式,我们第一次需要用的时候才new一个对象出来。
public class SingletonTest {
private static SingletonTest instance;
/**
* 将构造方法改为私有的,就无法new了
*/
private SingletonTest() {
}
public static synchronized SingletonTest getInstance() {
if (instance == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new SingletonTest();
}
return instance;
}
/**
* 模拟并发情况,100个线程同时获取实例
*
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
SingletonTest instance = SingletonTest.getInstance();
System.out.println(instance.hashCode());
}
}).start();
}
}
}
为了解决上面写法效率慢的问题,就产生了下面这种方式
public class SingletonTest {
private static SingletonTest instance;
/**
* 将构造方法改为私有的,就无法new了
*/
private SingletonTest() {
}
public static SingletonTest getInstance() {
//双重判断,主要是为了解决第一次获取实例时的并发访问
//以后获取实例的时候,不会运行加锁的代码,就不会影响效率了
if (instance == null) {
synchronized (SingletonTest.class) {
if (instance == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new SingletonTest();
}
}
}
return instance;
}
/**
* 模拟并发情况,100个线程同时获取实例
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
SingletonTest instance = SingletonTest.getInstance();
System.out.println(instance.hashCode());
}
}).start();
}
}
}
还有一种比上面更完美的写法(推荐使用)
public class SingletonTest {
/**
* 这种方式是利用了jvm加载类只加载一次来保证线程安全的
* 而且静态内部类在外部类被加载的时候不会被加载
*/
private static class SingletonHolder {
private final static SingletonTest instance = new SingletonTest();
}
/**
* 将构造方法改为私有的,就无法new了
*/
private SingletonTest() {
}
public static SingletonTest getInstance() {
return SingletonHolder.instance;
}
/**
* 模拟并发情况,100个线程同时获取实例
*
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
SingletonTest instance = SingletonTest.getInstance();
System.out.println(instance.hashCode());
}
}).start();
}
}
}
最后一种就是effective java这本书中介绍的一种方法,这种方式也是线程安全的
public enum SingletonTest {
instance;
/**
* 在枚举中写正常的业务方法
*/
public void m(){
//该类正常的业务逻辑
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
SingletonTest instance = SingletonTest.instance;
System.out.println(instance.hashCode());
}
}).start();
}
}
}
2 策略模式
现在有一个需求:用户登录后需要将用户信息缓存保存在内存中,还有用户的权限信息
public class UserService1 {
//内存
Map<String, Object> cache = new HashMap<String, Object>();
/**
* 登录
*/
public void login() {
User user = new User();
user.setAge(13);
user.setName("test");
//登录成功了,要将用户信息保存到缓存中
cache.put(user.getName(), user);
}
/**
* 授权
* 保存权限信息
*/
public void authentizate() {
//授权成功,要将用户权限信息保存到缓存中
cache.put("test-perm", "admin");
}
}
模拟认证授权
public class Main {
public static void main(String[] args) {
UserService1 userService1 = new UserService1();
//模拟前端的登录请求
userService1.login();
//授权
userService1.authentizate();
//查看内存中缓存信息
System.out.println(userService1.cache);
}
}
这样写是没问题的吧。
项目上线一段时间后,发现用户人数过大,缓存放在内存中服务器压力过大,现在需要将以前所有保存到内存中的缓存保存到redis中。所以,我们是不是需要在每个方法上都修改一遍,将原来的cache.put();
方法换为redisTemplate.opsForValue().set()
,但是你要注意,我们项目中肯定不止两个地方用缓存,而且缓存也不仅仅只有添加的方法(增删改查,这是最基本的)。这样看起来那任务量可就大了,还容易出错。也不符合我们面向对象设计的开闭原则(对扩展开放,对修改关闭)。
现在我们使用策略模式优化一下
抽象一个接口,里面定义缓存的增删改查
public interface CacheDao {
void add(String key,Object value);
void delete(String key);
void get(String key);
}
内存
public class MemoryDao implements CacheDao {
//内存
Map<String, Object> cache = new HashMap<String, Object>();
public void add(String key, Object value) {
cache.put(key, value);
}
public void delete(String key) {
cache.remove(key);
}
public Object get(String key) {
return cache.get(key);
}
}
redis
public class RedisDao implements CacheDao {
private RedisTemplate<String,Object> redisTemplate;
public void add(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public void delete(String key) {
redisTemplate.delete(key);
}
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
}
具体业务类中使用
public class UserService2 {
/**
* 我们需要用哪种缓存策略,我们就传入对应策略的对象
*
*/
private CacheDao cacheDao;
public CacheDao getCacheDao() {
return cacheDao;
}
public void setCacheDao(CacheDao cacheDao) {
this.cacheDao = cacheDao;
}
/**
* 登录
*/
public void login() {
User user = new User();
user.setAge(13);
user.setName("test");
//登录成功了,要将用户信息保存到缓存中
cacheDao.add(user.getName(), user);
}
/**
* 授权
* 保存权限信息
*/
public void authentizate() {
//授权成功,要将用户权限信息保存到缓存中
cacheDao.add("test-perm", "admin");
}
}
模拟认证授权
public class Main {
public static void main(String[] args) {
UserService2 userService2 = new UserService2();
//切换只需要修改这一处代码
userService2.setCacheDao(new RedisDao());
userService2.login();
userService2.authentizate();
//查看内存中缓存信息
}
}
对于策略模式,下面这个例子也适用
现在有一个需求:做一个植物大战僵尸的游戏,初期需求就两个角色
僵尸 | 行动方式 | 攻击方式 |
---|---|---|
普通僵尸 | 走 | 用手抓 |
帽子僵尸 | 走 | 用帽子打 |
后期,我们需要需要增加几款僵尸
僵尸 | 行动方式 | 攻击方式 |
---|---|---|
铁桶僵尸 | 走 | 用铁桶防御 |
撑杆僵尸 | 跳 | 用杆子跳过去 |
行动方式和攻击方式是不是也可以看成一种策略,没有必要每种僵尸都写一次,而死抽象成一个接口,由接口实现具体的攻击方式或行动方式。僵尸抽象类聚合这两个接口。
//抽象僵尸类
public abstract class Zombie {
//聚合两个策略接口
private Attackable attackable;
private Moveable moveable;
//僵尸开始向前攻击
abstract public void display();
}
public interface Attackable {
void attack();
}
public interface Moveable {
void move();
}
写到这里是不是都明白了,我们只需要实现这两个接口,实现具体的攻击方式,具体的行动方式,然后就可以做到组件复用,修改方便。
3 工厂模式
工厂设计模式有4种,简单工厂,静态工厂,工厂方法,抽象工厂
3.1 简单工厂
工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式
产品需要有一个共同的父类或接口
就比如,我现在有一个车间,它生产两种类型的鼠标A
和B
,当我向车间控制台输入A
的时候,车间就给我生产一个A类型鼠标,输入B
的时候就给我生产一个B类型鼠标。这样,这个车间就相当于一个简单工厂。
public class SimpleFactory {
/**
* 车间控制台
*/
Mouse getMouse(String type){
if ("A".equalsIgnoreCase(type)){
return new AMouse();
}else if ("B".equalsIgnoreCase(type)){
return new BMouse();
}
return null;
}
}
/**
* 共同接口
*/
public interface Mouse {
void type();
}
public class AMouse implements Mouse {
public void type() {
System.out.println("A类型鼠标");
}
}
public class BMouse implements Mouse {
public void type() {
System.out.println("B类型鼠标");
}
}
通过工厂创建
Mouse mouse1 = new SimpleFactory().getMouse("A");
Mouse mouse2 = new SimpleFactory().getMouse("B");
3.2 静态工厂
简单工厂每次需要获取工厂对象才能创建所需的对象,静态工厂就不用了。
public class StaticFactory {
public static Mouse getMouse(String type){
if ("A".equalsIgnoreCase(type)){
return new AMouse();
}else if ("B".equalsIgnoreCase(type)){
return new BMouse();
}
return null;
}
}
通过工厂创建
Mouse mouse1 = StaticFactory.getMouse("A");
Mouse mouse2 = StaticFactory.getMouse("B");
3.3 工厂方法
定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类
举个例子:还是要生产两种型号的鼠标,但是我现在不在一个车间生产了,我决定一个车间只生产一款型号,你需要哪个型号的鼠标,我就去建造生产哪种鼠标的车间,然后去那个车间拿。
/**
* 准备一个地方,用来生产鼠标,但是现在还没有生产的设备
* 等到用户决定要哪种鼠标,再购买对应的设备
*
*/
public interface Creator {
Mouse factoryMethod();
}
public interface Mouse {
void type();
}
public class AMouse implements Mouse {
public void type() {
System.out.println("A类型鼠标");
}
}
public class BMouse implements Mouse {
public void type() {
System.out.println("B类型鼠标");
}
}
现在我需要A类型的鼠标,那么我就在那块地方建一个A鼠标生产车间
public class AMouseCreator implements Creator {
public Mouse factoryMethod() {
return new AMouse();
}
}
//现在就可以通过这个车间拿到鼠标了
Mouse mouse = new AMouseCreator().factoryMethod();
生产一段时间,发现需要B类型的鼠标,那么我就在那块地方再建一个B鼠标生产车间
public class BMouseCreator implements Creator {
public Mouse factoryMethod() {
return new BMouse();
}
}
//现在就可以通过这个车间拿到鼠标了
Mouse mouse = new BMouseCreator().factoryMethod();
3.4 抽象工厂
到了抽象工厂这里,就不是简简单单生产一种类型的产品了,而是产品族。
举个例子:现在工厂生产两款产品,4个型号分别是A鼠标,B鼠标,A键盘,B键盘
,现在产品不单卖,而是组合成一个套装,目前有2种组合(AA 上班族,BB 游戏党),用户需要那样的组合就购买哪种。以前是这样的,生产上班族套装,那么我就得去鼠标车间调整生产A鼠标,去键盘车间调整生产A键盘。过一段时间,要生产游戏党套装,那是不是又得去所有车间,都调整一遍。这个小公司才两种产品,如果是大公司呢,它还要生产主机,生产显示屏,它们也有不同的型号,10种产品组合成不同套装,每次切换生产套装的时候所有车间都得跑一趟,累还容易出错。
AMouse aMouse = new AMouse();
aMouse.type();
AKeyBoard aKeyBoard = new AKeyBoard();
aKeyBoard.type();
现在不这么干了,我们生产线的控制台上定两个模板,需要生产哪个套装就直接切换到对应模板,一间切换,是不是很省事
/**
* 生产套装的模板
*/
public interface AbstractFactory {
//生产键盘
KeyBoard createKeyBoard();
//生产鼠标
Mouse createMouse();
}
/**
* 上班族套装
*/
public class ASuitFactory implements AbstractFactory {
public KeyBoard createKeyBoard() {
return new AKeyBoard();
}
public Mouse createMouse() {
return new AMouse();
}
}
/**
* 游戏党套装
*/
public class BSuitFactory implements AbstractFactory {
public KeyBoard createKeyBoard() {
return new BKeyBoard();
}
public Mouse createMouse() {
return new BMouse();
}
}
这样使用就简单了
//直接切换模板
AbstractFactory SuitFactory = new ASuitFactory();
//AbstractFactory SuitFactory = new BSuitFactory();
Mouse mouse=SuitFactory.createMouse();
KeyBoard keyBoard=SuitFactory.createKeyBoard();
mouse.type();
keyBoard.type();
3.5 总结
对于工厂模式,我们没有必要死扣概念,能够生产对象的都可以称之为工厂。工厂模式有很多好处,比如我们可以将对象创建后的属性设置写到工厂中,这样创建的对象就都有默认值了。
4 观察者模式
模拟小孩哭了,然后爸爸起来抱抱,妈妈起来喂奶的场景。
不使用观察者模式,这种模式有个严重问题,主线程一直循环,浪费资源
public class Main {
public static void main(String[] args) {
final Kid kid = new Kid();
Father father = new Father();
Mon mon = new Mon();
//10秒后孩子开始哭
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(10000);
//小孩开始哭了,自动通知爸爸妈妈
kid.cry();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//主线程一直循环,监视孩子是否哭泣
while (true){
System.out.println("盯着孩子");
if (kid.isCry()){
father.action();
mon.action();
break;
}
}
}
}
public class Father{
public void action() {
System.out.println("baba起来抱着");
}
}
public class Mon{
public void action() {
System.out.println("mama起来喂奶");
}
}
public class Kid {
private boolean cry = false;
public boolean isCry() {
return cry;
}
public void setCry(boolean cry) {
this.cry = cry;
}
public void cry() {
cry = true;
System.out.println("哭哭哭");
}
}
观察者模式
public class Main {
public static void main(String[] args) {
Kid kid = new Kid();
//给小孩设置观察者
kid.observers.add(new Father());
kid.observers.add(new Mon());
//小孩开始哭了,自动通知爸爸妈妈
kid.cry();
}
}
public interface Observer {
/**
* 小孩醒了,通知观察者
*/
void wakeup();
}
public class Father implements Observer{
public void wakeup() {
System.out.println("baba起来抱着");
}
}
public class Mon implements Observer {
public void wakeup() {
System.out.println("mama起来喂奶");
}
}
public class Kid {
List<Observer> observers = new ArrayList<Observer>();
private boolean cry = false;
public void cry() {
cry = true;
System.out.println("哭哭哭");
//通知所有的观察者
for (Observer observer : observers) {
observer.wakeup();
}
}
}
其实要点就是被观察者中需要设置观察者列表,当被观察者做了某个动作的时候,通知观察者列表中的所有观察者。
在正常设计中,一般不会这么简单,观察者会根据被观察者的行为的不同做出不同的反应。就比如小孩的哭,有嚎啕大哭,有随便哭两声,不同的哭爸爸妈妈应该有不同的反应。
关键代码
//事件驱动
public class WakeupEvent {
//哭的时间
long timestamp;
//哭的程度
String loc;
public WakeupEvent() {
}
public WakeupEvent(long timestamp, String loc) {
this.timestamp = timestamp;
this.loc = loc;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
public interface Observer {
/**
* 小孩醒了,通知观察者
* 每次通知需要传入事件驱动
*/
void wakeup(WakeupEvent event);
}
public class Kid {
List<Observer> observers = new ArrayList<Observer>();
private boolean cry = false;
public void cry() {
cry = true;
System.out.println("哭哭哭");
WakeupEvent event = new WakeupEvent(2132, "daku");
//通知所有的观察者
for (Observer observer : observers) {
observer.wakeup(event);
}
}
}
注意:observer,listener,hook,callback这些全是观察者。
5 责任链模式
web中的过滤器链就是它的典型实现。每一个过滤器负责一个功能,只有所有的过滤器都通过之后,才能处理业务逻辑,一个过滤器没有通过,就沿原路返回。下面就模拟一下过滤器链。
public class Request {
}
public class Response {
}
public interface Filter {
/**
* 模拟过滤器
* @param request
* @param response
* @param filterChain
*/
void doFilter(Request request,Response response,FilterChain filterChain);
}
public class FilterChain {
//需要通过的过滤器
List<Filter> filters=new ArrayList<Filter>();
//当前执行到第几个过滤器
private int index=0;
public FilterChain() {
}
public FilterChain addFilter(Filter filter){
filters.add(filter);
return this;
}
//这个方法的名字可以随意取,我这里叫这个名字是为了模拟web中的过滤器链
//实际使用时,两个名字一般不相同
void doFilter(Request request, Response response){
if ((index!=filters.size()-1)&&filters.size()!=0){
//不是最后一个过滤器
Filter filter = filters.get(index);
index++;
filter.doFilter(request,response,this);
}else{
//所有过滤器执行完了之后,开始执行自己的业务逻辑,然后再沿原路返回
}
}
}
public class StringFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain filterChain) {
//对request做一系列处理
//放行
filterChain.doFilter(request,response);
//对response做一系列处理
}
}
public class CharacterFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain filterChain) {
//对request做一系列处理
//放行
filterChain.doFilter(request,response);
//对response做一系列处理
}
}
使用
//模拟controller中的request和response
Request request = new Request();
Response response = new Response();
//创建一个过滤器链,并添加过滤器
FilterChain filterChain = new FilterChain()
.addFilter(new StringFilter())
.addFilter(new CharacterFilter());
//执行过滤器链
filterChain.doFilter(request,response);
6 装饰者模式
装饰者模式你可以理解为给已经做好的东西加点佐料。
举个例子:去书店买书,以前是直接购买,先在,我需要先验证书是否是正版的,按照传统的编程方式就是直接继承,重写买书方法,在买书方法中加入验证正版的代码。这种方式明显不好,如果我们还需要添加一个功能(指定出版社),是不是又得继承一次,那如果我们只需要指定出版社,不要验证正版这个功能呢,很明显就产生了类爆炸。
我们现在使用装饰者模式,装饰者模式抽象出一个买书的接口,业务类需要实现该接口,装饰类也需要实现该接口,并且装饰类中需要一个业务类的属性。
/**
* 抽象接口,定义了买书方法
*/
public interface Purchasing {
void purchase();
}
/**
* 用户付钱买书
*/
public class User implements Purchasing {
public void purchase() {
System.out.println("购买一本书");
}
}
/**
* 抽象装饰类
*/
public abstract class Decorator implements Purchasing{
/**
* 可以是被装饰类,也可以是装饰类
*/
private Purchasing purchasing;
/**
* 抽象的买书方法
*/
public abstract void purchase();
public Purchasing getPurchasing() {
return purchasing;
}
public void setPurchasing(Purchasing purchasing) {
this.purchasing = purchasing;
}
}
/**
* 正版
*/
public class GenuineDecorator extends Decorator{
@Override
public void purchase() {
//验证是否正版
System.out.println("是正版");
getPurchasing().purchase();
}
}
/**
* 出版社
*/
public class PublishDecorator extends Decorator{
@Override
public void purchase() {
//验证是否是某个出版社
System.out.println("是人社");
getPurchasing().purchase();
}
}
测试
//被装饰类对象
User user = new User();
//装饰器
Decorator decorator = new PublishDecorator();
//被装饰类对象设置到装饰器中
decorator.setPurchasing(user);
//装饰成功
decorator.purchase();
7 适配器模式
举个例子: 手机充电电压是5v,而家庭电压是220v,是不能直接充电的,这时候就需要借助充电器将220v转化为5v,这就是典型的适配器模式。
/**
* 该类表示家庭用电电压
*/
public class Voltage220v {
public int output220v(){
System.out.println("输出220v电压");
return 220;
}
}
/**
* 充电电压接口
*/
public interface Input5v {
int input5v();
}
/**
* 手机充电输入电压5v
*/
public class Voltage5v implements Input5v {
public int input5v() {
System.out.println("手机充电电压5v");
return 5;
}
}
/**
* 5v正常充电
*/
public class phone {
public void charge(Input5v input5v){
if (input5v.input5v()==5){
System.out.println("充电成功");
}else {
System.out.println("充电失败");
}
}
}
//接下来是重点了,适配器将220v转化为5v
public class VoltageAdapter implements Input5v {
//220v电压
private Voltage220v voltage220v;
public VoltageAdapter(Voltage220v voltage220v) {
this.voltage220v = voltage220v;
}
public int input5v() {
int original = voltage220v.output220v();
System.out.println("原电压为:"+original+"v");
int current = original / 44;
System.out.println("转换后的电压为:"+current+"v");
return current;
}
}
测试
phone phone = new phone();
//适配器将220v转化为5v
VoltageAdapter voltageAdapter = new VoltageAdapter(new Voltage220v());
//充电
phone.charge(voltageAdapter);
具体应用:io流
//字节流
FileInputStream fileInputStream = new FileInputStream("D:\\spring-context.xml");
//典型的适配器模式,将字节流转化为字符流
InputStreamReader reader = new InputStreamReader(fileInputStream);
//字符流
BufferedReader bufferedReader = new BufferedReader(reader);
String s = bufferedReader.readLine();
System.out.println(s);
8 模板方法模式
一个抽象类中定义了执行它的方法的方式,它的子类可以按照需要重写方法实现,但调用将以抽象类中定义的方法进行。
举个例子:要开发一个游戏,游戏分为三个部分,初始化,开始游戏,结束游戏
public abstract class Game {
//初始化
abstract void initialze();
//开始游戏
abstract void startPlay();
//结束游戏
abstract void endPlay();
/**
* 具体业务逻辑由子类实现
* 这里只是定义了方法的执行方式
*/
public void play(){
initialze();
startPlay();
endPlay();
}
}
spring源码中BeanFactory就是典型的模板方法设计模式
9 代理模式
代理分为静态代理和动态代理,静态代理非常简单,这里就不说了,这里重点是动态代理。
动态代理其实就是在程序运行过程中动态的生成代理类,代理类的生成方式无非就是通过字符串拼接代理类内容和io流生成一个代理类文件,然后使用类加载器加载到jvm中,最后使用反射生成代理类对象。(真实的其实不是这样,jdk动态代理生成的代理类是通过asm
框架直接操作字节码文件实现的,节省了编译过程,效率更好)
动态代理有两种实现方式,一种是jdk动态代理,还有一种是cglib动态代理。
9.1 jdk动态代理
jdk实现代理的方式是代理类和被代理类要实现同一个接口
//共同接口
public interface Subject {
void sell();
}
//被代理类
public class User implements Subject {
public void sell(){
System.out.println("买书");
}
}
生成代理类
final User user = new User();
//这个方法返回代理类对象
Subject subject = (Subject) Proxy.newProxyInstance(
//使用被代理类的类加载器
User.class.getClassLoader(),
//共同接口
new Class[]{Subject.class},
//与代理类关联的调用逻辑,内部类
new InvocationHandler() {
/**
* 这个方法处理代理类的业务逻辑
* 此处就是什么时候打印日志
*
* @param proxy 代理类对象
* @param method 被代理的方法的对象
* @param args 被代理的方法的参数
* @return 返回被代理类方法的执行结果
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//实现业务逻辑,比如日志
System.out.println("日志打印");
//执行被代理类的被代理方法
return method.invoke(user, args);
}
});
subject.sell();
生成的代理类
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
* 静态代码块,对象创建的时候执行
*/
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
//m3是公共接口中sell方法的对象
m3 = Class.forName("cn.lx.proxy.jdk.Subject").getMethod("sell");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
/**
* 代理类的sell方法
*/
public final void sell() throws {
try {
//h是你传入的InvocationHandler对象
//执行该对象的invoke方法
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
保存代理类
//加入该行代码可使代理类保存到项目根路径下
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //设置系统属性
9.2 cglib动态代理
cglib相比于jdk而言简单一些。它的原理就是继承,重写父类方法。
下面我使用cglib实现上面的相似的代理。
需要导包
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
被代理类
public class User {
public void sell(){
System.out.println("买书");
}
}
生成代理类
//被代理类对象
final User user = new User();
//增强器
Enhancer e = new Enhancer();
//设置被代理类的父类
e.setSuperclass(User.class);
//设置回调(方法拦截器)
e.setCallback(new MethodInterceptor() {
/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
*
* @param obj "this", the enhanced object
* @param method intercepted Method
* @param args argument array; primitive types are wrapped
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @see MethodProxy
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("代理前");
//执行被代理类的被代理方法
Object invoke = method.invoke(user, args);
System.out.println("代理后");
return invoke;
}
});
//创建代理类
User proxy = (User) e.create();
//调用
proxy.sell();
我们看一下生成的代理类源码(省略equals,tostring,hashcode clone方法)
public class User$$EnhancerByCGLIB$$fb483fa1 extends User implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$sell$0$Method;
private static final MethodProxy CGLIB$sell$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
/**
* 静态代码块,创建对象的时候会调用
*/
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//获取代理类的class对象
Class var0 = Class.forName("cn.lx.proxy.cglib.User$$EnhancerByCGLIB$$fb483fa1");
//object类的方法对象
Class var1;
//这里是获取equals,tostring,hashcode clone 的方法对象
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
//获取user类sell方法的对象
CGLIB$sell$0$Method = ReflectUtils.findMethods(new String[]{"sell", "()V"}, (var1 = Class.forName("cn.lx.proxy.cglib.User")).getDeclaredMethods())[0];
CGLIB$sell$0$Proxy = MethodProxy.create(var1, var0, "()V", "sell", "CGLIB$sell$0");
}
final void CGLIB$sell$0() {
super.sell();
}
public final void sell() {
//获取方法拦截器,通过enhancer创建代理类的时候,会将方法拦截器(回调函数)
//注入到代理对象中
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//这里是重点,执行方法拦截器的intercept方法
var10000.intercept(this, CGLIB$sell$0$Method, CGLIB$emptyArgs, CGLIB$sell$0$Proxy);
} else {
super.sell();
}
}
/**
* 这个方法作用是获取回调函数
*
* @param var0
*/
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
User$$EnhancerByCGLIB$$fb483fa1 var1 = (User$$EnhancerByCGLIB$$fb483fa1) var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) var10000)[0];
}
}
/**
* 通过enhancer设置回调函数,也就是方法拦截器
*
* @param var1
* @param var2
*/
public void setCallback(int var1, Callback var2) {
switch (var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor) var2;
default:
}
}
}
保存代理类
//加入该行代码可使代理类保存到c盘class路径下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
//设置系统属性
未完待续。。。。。。。。。。。。