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 ...
随机推荐
- 不允许还有Java程序员不了解BlockingQueue阻塞队列的实现原理
我们平时开发中好像很少使用到BlockingQueue(阻塞队列),比如我们想要存储一组数据的时候会使用ArrayList,想要存储键值对数据会使用HashMap,在什么场景下需要用到Blocking ...
- CSS clear both清除浮动
.clear{clear:both;} <div class="clear"></div>
- settings.py 配置汇总
数据库配置: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 数据库引擎 'NAME': ' ', #数据库名称 ...
- 8_vue是如何进行数据代理的
在了解了关于js当中的Object.defineProperty()这个方法后,我们继续对vue当中的数据代理做一个基于现在的解析 建议观看之前先了解下js当中的Obejct.defineProper ...
- CF39H
前言 谁来给我讲讲九九乘法表啊. 以上菲克向. \(\sf{Solution}\) 看题上来就是数据范围 \(2\leq k\leq 10\) ,显然打表可以轻松水过,数据这么小,手算是没问题的啦. ...
- 中小型企业综合项目(Nginx+LVS+Tomcat+MGR+Nexus+NFS)
Nginx+Tomcat+Mysql综合实验 1.环境准备 服务器 IP地址 作用 系统版本 数据库服务器1 192.168.100.111 MGR集群数据库master节点 Rocky8.6 数据库 ...
- 网页状态码(HTTP状态码)。
网页状态码(HTTP状态码). 状态码 说明 详情 100 继续 请求者应当继续提出请求.服务器已收到请求的一部分,正在等待其余部分. 101 切换协议 请求者已要求服务器切换协议,服务器已确认并准备 ...
- 常用到的read命令
记录一下. 几个简单参数介绍 read -p :显示提示信息 read -s :静默模式(Silent mode),不会在屏幕上显示输入的字符.当输入密码和其它确认信息的时候,这是很有必要的. rea ...
- ABAP 调用HTTP上传附件
1.需求说明 在SAP中调用第三方文件服务器的HTTP请求,将文件保存在文件服务器上,并返回保存的文件地址.SAP保存返回的文件地址,通过浏览器进行访问. 2.需求实现 2.1.POSTMAN测试 通 ...
- Python用yield form 实现异步协程爬虫
很古老的用法了,现在大多用的aiohttp库实现,这篇记录仅仅用做个人的协程底层实现的学习. 争取用看得懂的字来描述问题. 1.什么是yield 如果还没有怎么用过的话,直接把yield看做成一种特殊 ...