Java进阶篇——设计模式
设计模式
一、代理模式
使用代理类对真实对象进行代理,包括真实对象方法的调用、功能的扩展等。访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行扩展。类似于中介在卖家和买家之间的角色。
代理模式的角色主要有:抽象角色、真实角色、代理角色
1.静态代理
以张三到二手平台售卖二手电脑为例,张三为真实角色,二手平台为代理角色
抽象角色:
public interface User {
void sell();//购买方法
}
真实角色:
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserImpl implements User{
private String name="张三";
private String thing="二手电脑";
@Override
public void sell() {
System.out.println(name+"要卖掉"+thing);
}
}
代理角色:
@Component
public class UserProxy implements User {
UserImpl user;
@Override
public void sell() {
before();
user.sell();
after();
}
//扩展
public void before(){
System.out.println("包装了"+user.getThing());
}
public void after(){
System.out.println("售后服务");
}
}
测试:
@Component
public class StaticProxyTest {
public static void main(String[] args) {
new UserProxy(new UserImpl()).sell();
}
}
看起来似乎很简单,只是加了一层代理类就实现了扩展功能,但实际上维护时非常困难的。如果用户此时新增了需求,要在平台上买东西。那么直接带来了大量的代码工作,效率很低。也就产生了动态代理。
2.动态代理
和静态代理不同的是,动态代理的代理类是spring为我们生成的。相当于mybatisplus和mybatis的区别。
代理类生成工具类
public class DynamicProxy {
/*jdk动态代理
代理类需要是接口实现类impl.getClass().getInterfaces(),接收代理实例也是用接口来接收
通过反射Instance+拦截器Handler实现
jdk自带的代理支持
*/
public static Object jdkProxy(final Object impl){
Object proxyInstance = Proxy.newProxyInstance(impl.getClass().getClassLoader(), impl.getClass()
.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke=null;
System.out.println("商品包装");
invoke = method.invoke(impl, args);
System.out.println("售后服务");
return invoke;
}
});
return proxyInstance;
}
/*CGlib动态代理
通过对类继承来实现,无需接口实现
第三方工具,基于ASM实现
*/
public static Object CGlibProxy(final Object impl){
Object proxyInstance = Enhancer.create(impl.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = null;
System.out.println("商品包装");
invoke = method.invoke(impl, objects);
System.out.println("售后服务");
return invoke;
}
});
return proxyInstance;
}
}
又或者可以实现对应的接口,以InvocationHandler举例
public class DynamicProxy implements InvocationHandler {
private User user;
public Object getProxyInstance(User user){
this.user=user;
return Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
System.out.println("商品包装");
invoke = method.invoke(user, args);
System.out.println("售后服务");
return invoke;
}
}//实际使用中只需要调用getProxyInstance方法,丢入一个原始类即可
使用:
public class DynamicProxyTest {
public static void main(String[] args) {
UserImpl user = new UserImpl();
User o = (User)DynamicProxy.jdkProxy(user);
o.sell();
// UserImpl o1 = (UserImpl)DynamicProxy.CGlibProxy(user);
// o1.sell();
}
}
3.Spring AOP
springaop便是动态代理实践的一个典例,不改变方法原有的代码,实现对方法功能的增强,使用aop之前,对aop相关的概念是一定要了解清楚的。
(1)aop相关概念
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 接入点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切入点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的抽象集合,一般以类的形式呈现。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
(2)springaop注解开发
由于目前来说,注解开发是最简单快捷的,这里只介绍注解开发,我们只需要知道底层是用动态代理实现的即可。
创建切面类和通知
只需要在类上添加@Aspect注解
@Aspect
public class MyAspect {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
创建并注入切点
@Aspect
public class MyAspect {
//表示将com.amlia.service包下的所有的类的所有方法(任何参数)定义为切点
@Pointcut("execution(* com.amlia.service.*.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void before(){
System.out.println("方法执行前");
}
//也可以直接在通知上面定义切点
@After("execution(* com.amlia.service.*.*(..))")
public void after(){
System.out.println("方法执行后");
}
}
除了before和after类型的通知外,还有其他类型
@Before:方法执行前通知
@After:方法执行后通知
@Around:方法环绕通知
@AfterReturning:方法返回后通知
@AfterThrowing:方法错误抛出之后
可以测试他们的执行顺序:
(3)aop的应用
打印日志(方法执行前后打印参数方法名返回值或者调用关系等信息)
日志级别:
- OFF 关闭日志
- FATAL 较严重的问题,高于ERROR
- ERROR 打印错误信息
- WARN 打印告警信息
- INFO 打印日常信息
- DEBUG 打印调试信息
- TRACE 打印追踪信息
- ALL 打印所有信息
性能检测(方法执行前和方法执行后分别进行时间截取求差值)
事务控制(抛出错误后进行事务回滚)
权限控制(方法执行前检测用户是否有权限)
二、单例模式
单例模式顾名思义就是该类只能有一个实例,而且是被类自己创建的。外界不能访问该类的构造方法,因为他是私有的。这种模式为了解决单个类频繁的创建和销毁的情况或者说是某种单个实例的场景,比如只能有一个中国实例,并且很多框架底层都使用了单例模式,比如bean的生命周期中就有singleton单例。
单例模式有很多实现方式,为了适应不同种情况:
1.懒汉模式
客人点单了厨师才开始做菜,线程不安全,如果需要线程安全,单体架构下,在方法上加synchronize关键字即可,多体架构下,方法体内加分布式锁。
public class Singleton {
private static Singleton instance;
private Singleton (){}//构造方法私有
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();//需要时加载——懒式加载
}
return instance;
}
}
2.饿汉模式
厨师提前做好菜,客人点了直接上菜。由于类加载过程中是阻塞等待机制,所以是线程安全的。缺点是产生了大量的"垃圾"对象,比较占用资源。
public class Singleton {
private static Singleton instance = new Singleton();//加载时初始化
private Singleton (){}//构造方法私有
public static Singleton getInstance() {
return instance;
}
}
3.双重校验的单例模式
大部分情况下,懒汉饿汉已经满足了。但是如果要求多线程下安全且高性能,那么还有一个较为复杂的模式
public class Singleton {
//volatile防止初始化指令重排,导致其他线程误以为初始化完成,空指针
private volatile static Singleton singleton;排
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {//提升性能
synchronized (Singleton.class) {
if (singleton == null) {//防止阻塞等待下实例化已经完成的情况
singleton = new Singleton();
}
}
}
return singleton;
}
}
4.静态内部类单例模式
这种方法同样利用了类加载机制的线程安全,但是巧妙的规避了资源的浪费。他利用内部类静态域的加载特性(使用时加载)达到了懒汉饿汉结合的效果。
public class Singleton {
private static class SingletonHolder {
//静态内部类的静态域
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Java进阶篇——设计模式的更多相关文章
- Java进阶篇 设计模式之十四 ----- 总结篇
前言 本篇是讲述之前学习设计模式的一个总结篇,其目的是为了对这些设计模式的进行一个提炼总结,能够通过查看看此篇就可以理解一些设计模式的核心思想. 设计模式简介 什么是设计模式 设计模式是一套被反复使用 ...
- Java进阶篇设计模式之十三 ---- 观察者模式和空对象模式
前言 在上一篇中我们学习了行为型模式的备忘录模式(Memento Pattern)和状态模式(Memento Pattern).本篇则来学习下行为型模式的最后两个模式,观察者模式(Observer P ...
- Java进阶篇设计模式之十一 ---- 策略模式和模板方法模式
前言 在上一篇中我们学习了行为型模式的访问者模式(Visitor Pattern)和中介者模式(Mediator Pattern).本篇则来学习下行为型模式的两个模式,策略模式(Strategy Pa ...
- Java进阶篇设计模式之十 ---- 访问者模式和中介者模式
前言 在上一篇中我们学习了行为型模式的解释器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern).本篇则来学习下行为型模式的两个模式,访问者模式(Visito ...
- Java进阶篇设计模式之九----- 解释器模式和迭代器模式
前言 在上一篇中我们学习了行为型模式的责任链模式(Chain of Responsibility Pattern)和命令模式(Command Pattern).本篇则来学习下行为型模式的两个模式, 解 ...
- Java进阶篇设计模式之八 ----- 责任链模式和命令模式
前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...
- Java进阶篇设计模式之七 ----- 享元模式和代理模式
前言 在上一篇中我们学习了结构型模式的组合模式和过滤器模式.本篇则来学习下结构型模式最后的两个模式, 享元模式和代理模式. 享元模式 简介 享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能 ...
- Java进阶篇设计模式之二 ----- 工厂模式
前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...
- Java进阶篇设计模式之三 ----- 建造者模式和原型模式
前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...
- Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式
前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...
随机推荐
- 3.ElasticSearch系列之Docker本地部署
对于之前的部署方式一般用于生产环境,而对于学习而言Docker方式快速部署就好了,本示例在window10环境下进行. 1. Docker使用Elasticsearch 需要对vm.max_map_c ...
- 微服务 Zipkin 链路追踪原理(图文详解)
一个看起来很简单的应用,可能需要数十或数百个服务来支撑,一个请求就要多次服务调用. 当请求变慢.或者不能使用时,我们是不知道是哪个后台服务引起的. 这时,我们使用 Zipkin 就能解决这个问题. 由 ...
- ssh登录提示hosts is down
其他无用的网卡配置信息mv走重启network如果还是不行重启一下服务器问题就能解决
- MySQL索引报错
今天在MySQL 5.7版本的数据库中导库InnoDB表字段长度时遇到了"ERROR 1071 (42000): Specified key was too long; max key le ...
- 小样本利器4. 正则化+数据增强 Mixup Family代码实现
前三章我们陆续介绍了半监督和对抗训练的方案来提高模型在样本外的泛化能力,这一章我们介绍一种嵌入模型的数据增强方案.之前没太重视这种方案,实在是方法过于朴实...不过在最近用的几个数据集上mixup的表 ...
- LVS综合实验
LVS综合实验 1.环境准备 提前准备:Mysql8.0.30安装包.Mysql安装脚本.shopxo2.3.0安装包.DNS脚本 服务器 IP地址 作用 系统版本 Mysql-master 10.0 ...
- python 基本使用 异常判断
简单常用 isinstance 判断一个对象是否是一个已知的类型 arg=123 isinstance(arg, int) #输出True isinstance(arg, str) #输出False ...
- 2021-2022 ICPC, NERC, Northern Eurasia Onsite (Unrated, Online Mirror, ICPC Rules, Teams Preferred) J. Job Lookup
题意 n个节点,n<=200,你需要构造这n个几点成为一棵树,并且这棵树的中序遍历为1-n; 你构造树的节点之间的最短路构成一个n×n的最短距离矩阵d: 同时给你n×n的权重矩阵c:最最小的Σd ...
- pagehelper使用有误导致sql多了一个limit
接口测试报告中发现时不时有一个接口报错,但再跑一次又没有这个报错了.报错信息是sql异常,sql中有两个limit.查看后台代码和XXmapper.xml,发现确实只有一个limit.一开始开发以为是 ...
- layui文件上传
//前端代码 <div class="layui-form-item"> <label class="layui-form-label"> ...