一、简介
1、是什么
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结,为不断重复发生的问题,提供解决方案。
2、为什么
①、提高代码可重用性
②、提高代码的可读性
③、保障代码的可靠性
3、怎么用
- 使用场景:
①、在程序软件架构设计上使用(架构)
②、在软件架构设计上会使用(代码) - 优良系统设计特点:
①、可扩展性
②、灵活性(用和不用,不用太多增删代码)
③、组件化可插拔式(jar包依赖) - 设计原则:
名称 | 简介 | 例子 | 重要性 |
---|---|---|---|
单一职责原则 | 类职责单一,不要太多职责放到同一个类。(高内聚,低耦合) | userService 只写用户业务增删改,定时任务放别类 | ★★★★☆ |
开闭原则 | 软件实体对拓展开发,对修改关闭 | 基类不可修改 | ★★★★★ |
里氏代换原则 | 软件系统中,能接受父类,也能接受子类 | 通过new子类创建父类 | ★★★★☆ |
依赖倒转原则 | 抽象不依赖实现,实现依赖抽象 | 用到接口时,用依赖将其实现对象注入 | ★★★★★ |
接口隔离原则 | 多个专用接口替代一个统一的接口 | 业务接口,由一个个CRUD接口组成 | ★★☆☆☆ |
合成复用原则 | 多实用组合和聚合关联(注入),少继承 | spring 容器注入 | ★★★★☆ |
迪米特原则 | 软件实体使用第三者减低与现有对象的耦合度 | A依赖BCD——>A依赖E依赖BCD | ★★★☆☆ |
二、创建型模式
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”,单例、原型、工厂方法、抽
象工厂、建造者5种设计模式属于创建型模式。
1、单例(Singleton)模式
- 定义
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。 - 优点
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用(比如写文件操作)。
单例设计案例(饿汉式、懒汉式、双重验证)
public class SingleModel {
//1、单例类只能有一个实例。 A a = new A()
private static SingleModel singleModel = new SingleModel();
//2、单例类必须自己创建自己的唯一实例。
private SingleModel() {
}
//3、单例类必须给所有其他对象提供这一实例。
public static SingleModel getInstance(){
return singleModel;
}
}
案例一为基础的单例模式,但是存在问题——每次初始化时候会创建对象,不能达到使用才创建,不使用不创建。(饿汉式)
public class SingleModel2 {
//整个应用程序中只有一个自己的实例
private static SingleModel2 singleModel;
//只能自己创建自己
private SingleModel2() {
}
//需要提供一个方法让外界调用获取实例
public static synchronized SingleModel1 getInstance(){
if(singleModel==null){
singleModel = new SingleModel2();
}
return singleModel;
}
}
案例二,把new SingleModel下放到方法体内,但是由于出现问题,如果并发线程进来,那么会创建多个对象;于是上了方法锁,但是随之而来的是如果并发,不管有没有创建该对象,都会排队等待,效率低下。(懒汉式比这个少synchronized ,线程安全问题)
public class SingleModel3 {
//1、单例类只能有一个实例。
private static SingleModel3 singleModel;
//2、单例类必须自己创建自己的唯一实例。
private SingleModel3() {
}
//3、单例类必须给所有其他对象提供这一实例。
public static SingleModel3 getInstance(){
if(singleModel==null){
//控制多线程安全
synchronized (SingleModel3.class){
if(singleModel==null){
//对象为空,创建对应实例
singleModel = new SingleModel3();
}
}
}
return singleModel;
}
}
案例三,把方法锁改成对象锁+类锁,提高调用效率;但是引发问题如果第一个判断并发量大,都进去了,仍然会创建多个对象,于是加上了双重校验。(双重校验模式)
2、工厂设计(Factory Method)模式
- 定义
提供了一种 创建对象的最佳方式。它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
(例 A类里面需创建BCD实例,而一般BCD上会有接口实现,所以是AI a = new A(); BI b = new B(); CI c = new C();于是A类就依赖了6个类,那么我们把创建的事交给工厂F去创建,那么A只会依赖AI、BI、CI、F四个类)
- 优点
①、使用对象,只需要知道名称即可
②、屏蔽产品的具体实现,调用者只需专注接口
③、降低了耦合性
工厂模式案例(产品线生产)
//产品父类
public interface Product {
//查看产品详情
void show();
}
//汽车子类
public class Car implements Product {
@Override
public void show() {
System.out.println("汽车:比亚迪!");
}
}
//手机子类
public class Mobile implements Product {
//产品详情
@Override
public void show() {
System.out.println("手机:HUAWEI P40 Pro +");
}
}
public class ProductFactory {
/***
* 根据用户的需求创建不同的产品
* @return
*/
public static Product getBean(String name){
if(name.equals("mobile")){
return new Mobile();
}else if(name.equals("car")){
return new Car();
}
return null;
}
}
public class ProductFactoryTest {
public static void main(String[] args) {
Product mobile = ProductFactory.getBean("mobile");
Product car = ProductFactory.getBean("car");
mobile.show();
car.show();
}
}
输出结果:
手机:HUAWEI P40 Pro +
汽车:比亚迪!
Spring 中的 BeanFactory工厂模式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置文件中配置 定义bean-->
<bean id="car" class="com.xxx.factory.Car"/>
<bean id="mobile" class="com.xxx.factory.Mobile"/>
</beans>
public class BeanFactoryTest {
public static void main(String[] args) {
Resource resource = new ClassPathResource("spring-factory.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Product car1 = (Product) beanFactory.getBean("car");
Product car2 = (Product) beanFactory.getBean("car");
car1.show();
System.out.println(car1 == car2);
}
}
输出结果:
汽车:比亚迪!
true
此处可以看出,beanFactory帮我们创建了对象,并且这个对象是单例的,那么在Spring里面是怎么做到的呢?
public class AopTest {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("spring-aop.xml");
//此处getBean就是工厂模式中取对象的方法
UserServiceImpl userService = (UserServiceImpl) act.getBean("userService");
userService.add();
}
}
//进入getBean方法,因为getBeanFactory与getBean都是抽象方法,所以我们打断点进去
public Object getBean(String name) throws BeansException {
this.assertBeanFactoryActive();
return this.getBeanFactory().getBean(name);
}
//debug进入getBean
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
//发现此处会获取实例对象,我们点击方法进去
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if (sharedInstance != null && args == null) {
if (this.logger.isTraceEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//此处就是获取bean的方法,那么singletonObjects这个对象又是什么东西呢?
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
//搜索发现,原来这个对象为map ,它把bean名称作为key,实例作为value存储起来了
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
.......
小结:上面Spring初始化时候,把实例都存储到一个map上面了,要取实例的时候,使用bean 的id 到map 获取。
三、结构性型模式
1、代理(Proxy)模式
- 定义
给某对象提供一个代理对象,通过代理对象可以访问该对象的功能。主要解决通过代理去访问[不能直接访问的对象],(如: 租房中介,你可以直接通过中介去了解房东的房源信息,此时中介就可以称为代理。)
-
优点
①、职责清晰。 (房东收租,代理人处理房务)
②、高扩展性。 (房东可以增加处理人,也可以替换处理人)
③、智能化。(代理人更专业) -
缺点
①、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。(先交钱给中介,中介再给钱房东)
②、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。(找好的中介需要费时费力) -
案例
JDK动态代理
1、被代理的类必须实现一个接口
2、创建代理对象的时候,用JDK代理需要实现InvocationHandler
3、代理过程在invoke中实现
//1、被代理类必须实现一个接口,此处可以看做找中介的招工启事
public interface LandlordService {
//交房租
void pay(String username);
}
//此处是房东类,收房租是房东原来要做的事情
public class Landlord implements LandlordService{
//收房租
@Override
public void pay(String name){
System.out.println(name);
}
}
//此处为代理对象——中介
//2、创建代理对象的时候,用JDK代理需要实现InvocationHandler
public class QFang implements InvocationHandler {
//被代理的对象
private Object instance;
public QFang(Object instance) {
this.instance = instance;
}
/****
* 代理过程
* proxy:创建的代理的字节码->存在内存中
* method:用户调用的方法 (pay)
* args:被调用的方法参数
*/
//3、代理过程在invoke中实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
args[0]="QFang网好房源!带客户"+args[0];
//执行调用
Object result = method.invoke(instance,args);
return result;
}
}
//被代理的对象的类
public class LandlordServiceImpl implements LandlordService {
//被代理的方法
@Override
public void pay(String username) {
System.out.println(username+"来交租!");
}
}
public class QFangTest {
public static void main(String[] args) {
//1.指定被创建代理的对象
LandlordService landlordService = new LandlordServiceImpl();
//2.创建代理
LandlordService landlordServiceProxy =
(LandlordService) Proxy.newProxyInstance(
LandlordServiceImpl.class.getClassLoader(),
//被创建代理的对象实现的所有接口
LandlordServiceImpl.class.getInterfaces(),
new QFang(landlordService)
);
//3.调用代理中指定的方法
landlordServiceProxy.pay("王五");
}
}
处理结果:QFang网好房源!带客户王五来交租!
此处中介的处理方法已经代房东的处理方法进行处理了。
CGLib动态代理
1、代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现
2、通过Enhancer来创建代理对象
//房东
public class Landlord {
//收房租
public void pay(String name){
System.out.println(name);
}
}
//代理过程
//1、代理过程可以实现MethodInterceptor(Callback)接口中的invoke来实现
public class SFang implements MethodInterceptor{
//被代理的对象
private Object instance;
public SFang(Object instance) {
this.instance = instance;
}
/***
* 代理过程
* @param o
* @param method
* @param objects
* @param methodProxy : 方法的代理方法 $
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
objects[0] = "SFang房网,好房源,比Qfang好!又带客户"+objects[0];
//调用
Object result = method.invoke(instance, objects);
return result;
}
}
//被代理的对象的类,注意,此时不在实现接口方法,仅仅提供一个被代理方法
public class LandlordServiceImpl {
//被代理的方法
public void pay(String username) {
System.out.println(username+"来交租!");
}
}
public class SFangTest {
public static void main(String[] args) {
//1.被代理的对象
LandlordServiceImpl landlordService = new LandlordServiceImpl();
//2.创建代理对象
//1)被代理对象的字节码
//2)Callback代理过程对象
LandlordServiceImpl landlordServiceProxy = (LandlordServiceImpl) Enhancer.create(LandlordServiceImpl.class,new SFang(landlordService));
//3.调用代理对象方法
landlordServiceProxy.pay("赵六");
}
}
运行结果:SFang房网,好房源,比Qfang好!又带客户赵六来交租!
此处cglib比jdk动态代理少了抽象接口,是对jdk动态代理的封装。
Spring AOP-动态代理
代码目的:在调用新增方法前,执行日志记录
public interface UserService {
void add();
}
public class UserServiceImpl {
//@Override
public void add(){
System.out.println("增加用户!");
}
}
public class Log {
public void before(JoinPoint jp){
System.out.println("执行对象:"+jp.getSignature().getName());
System.out.println("前置通知记录日志!");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--userService-->
<bean id="userService" class="com.xxx.aop.service.UserServiceImpl"/>
<!--注册增强(切面)类-->
<bean id="log" class="com.xxx.aop.Log"/>
<!--AOP配置-->
<aop:config proxy-target-class="false">
<!--配置切面(切入点和通知的结合)-->
<aop:aspect ref="log">
<!--前置通知-->
<aop:before method="before" pointcut="execution(* com.xxx.aop.service.*.*(..))"></aop:before>
</aop:aspect>
</aop:config>
</beans>
public class AopTest {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("spring-aop.xml");
UserServiceImpl userService = (UserServiceImpl) act.getBean("userService");
userService.add();
}
}
执行结果:
执行对象:add
前置通知记录日志!
增加用户!
那么为什么配置切面后,会通知到log方法先执行日志记录呢
- 原理
- AOP动态代理源码集中在 DefaultAopProxyFactory 类中、
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
//此处,<aop:config proxy-target-class="false">跟config.isProxyTargetClass()关联
if(!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
//如果配置文件配false,那么此处会执行JDK动态代理
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
//此处,如果目标(userServiceImpl是否实现接口,实现了用JDK动态代理,没实现即用CGlib代理)
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
小结:从源码看出Spring动态代理是由JDK动态代理及CGlib代理实现。在执行add方法前,使用log类进行动态代理,打印出日志信息。
2、适配器(Adapter)模式
- 定义
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
- 优点
①、可以让任何两个没有关联的类一起运行。
②、提高了类的复用。
③、灵活性好。 - 缺点
过多地使用适配器,会让系统非常零乱,不易整体进行把握。(比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接 口的实现,一个系统如果太多出现这种情况,无异于一场灾难。)
适配器案例(打卡前身份验证)
public interface UserCardService {
void card(String name);
}
public class UserCard implements UserCardService {
@Override
public void card(String name){
System.out.println(name+"打卡成功!");
}
}
public class UserInfoBefore implements MethodBeforeAdvice {
//MethodBeforeAdvice:前置通知
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(args[0]+"身份识别通过!");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置文件中配置-->
<bean id="userCard" class="com.xxx.adapter.UserCard"/>
<bean id="userInfoBefore" class="com.xxx.adapter.UserInfoBefore"/>
<!--创建代理(也是一种适配模式)-->
<bean id="proxyBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--指定给谁创建代理-->
<property name="target" ref="userCard" />
<!--被代理的对象实现了哪些接口-->
<property name="interfaces" value="com.xxx.adapter.UserCardService" />
<!--指定拦截器-->
<property name="interceptorNames">
<list>
<value>userInfoBefore</value>
</list>
</property>
</bean>
public class AdapterTest {
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("spring-adapter.xml");
UserCardService userCardService = (UserCardService) act.getBean("proxyBean");
userCardService.card("王五");
}
}
输出结果:
王五身份识别通过!
王五打卡成功!
此处,proxyBean代理UserCard ,但是没有覆盖原先方法,所以打印出“王五打卡成功!”,而使用UserInfoBefore 作为拦截器,before方法先于代理方法执行,所以首先打印“王五身份识别通过!”。注意代理也是适配模式的一种,前置通知也是是适配器模式之一。那么Spring中是怎么通过前置通知的拦截器执行代码的呢?
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
Class var8;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
Boolean var18 = this.equals(args[0]);
return var18;
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var17 = this.hashCode();
return var17;
}
if (method.getDeclaringClass() != DecoratingProxy.class) {
Object retVal;
if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
}
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = target != null ? target.getClass() : null;
//此处进行拦截器获取,打断点可知此处会获取前置通知的拦截器
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//但是为什么会先处理before呢?进去执行方法
retVal = invocation.proceed();
}
@Nullable
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
} else {
//此处拦截器拦截,invoke
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}
public Object invoke(MethodInvocation mi) throws Throwable {
//此处先调用beforce方法
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
//真实方法调用
return mi.proceed();
}
小结:spring代理对象代理执行的时候,获取拦截器,进入拦截器的方法,先执行before方法,再执行真实方法。
3、享元(Flyweight)模式
- 定义:运用共享技术来有效地支持大量细粒度对象的复用。
- 享元模式和单利的区别:
单例是对象只能自己创建自己,整个应用中只有1个对象; 享元模式根据需要共享,不限制被谁创建(有可能有多个对象实例) - 优点:特定环境下,相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。(如:同线程某个对象只用相同的实例)
- 缺点:为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
享元模式案例(用户下单)
tips:此处ThreadLocal为线程对象,每个用户访问对应的线程均不一样,访问的ThreadLocal也不一样;且每次调用完毕后会进行销毁
//建立一个抽象对象,让共享对象继承(提高拓展性)
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public abstract class Session {
private String username;
private String name;
private String sex;
private String role;
private Integer level;
//额外操作
public abstract void handler();
}
//共享对象
public class SessionShare extends Session {
//方便创建实例
public SessionShare(String username, String name, String sex, String role, Integer level) {
super(username, name, sex, role, level);
}
@Override
public void handler() {
System.out.println("共享信息!");
}
}
//存储用户信息(存储共享对象)
@Component
public class SessionThreadLocal {
//1.创建一个ThreadLocal实现存储线程下共享的对象,此处可当做一个map,key为当前线程
private static ThreadLocal<Session> sessions = new ThreadLocal<Session>();
//2.添加共享对象
public void add(Session session){
sessions.set(session);
}
//3.获取共享对象
public Session get(){
return sessions.get();
}
//4.移除共享对象
public void remove(){
sessions.remove();
}
}
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Autowired
private ThreadSession threadSession;
/****
* 在拦截器中,将用户会话存储到ThreadLocal中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
//获取令牌
String authorization = request.getHeader("token");
//解析令牌
if(!StringUtils.isEmpty(authorization)){
Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);
//封装用户身份信息,存储到ThreadLocal中,供当前线程共享使用
//1.封装需要共享的信息[Session]
//2.创建一个对象继承封装信息,每次共享该对象 (不需要共享,则可以创建另外一个对象继承它)
//3.创建共享管理对象,实现共享信息的增加、获取、移除功能
threadSession.add(new SessionShare(
tokenMap.get("username").toString(),
tokenMap.get("name").toString(),
tokenMap.get("sex").toString(),
tokenMap.get("role").toString(),
Integer.valueOf(tokenMap.get("level").toString())
));
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
//输出令牌校验失败
response.setContentType("application/json;charset=utf-8");
response.getWriter().print("身份校验失败!");
response.getWriter().close();
return false;
}
/**
* 移除会话信息
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//移除用户信息
threadSession.remove();
}
}
/***
* 添加订单
* @param order
*/
@Override
public int add(Order order) {
//order.setUsername("wangwu");
order.setPaymoney(100); //结算价格
order.setMoney(100); //订单价格
//获取用户信息 此处把共享对象赋值出来
order.setUsername(threadSession.get().getUsername());
//装饰者模式嵌套运算计算价格
fullMoneySum.setMoneySum(orderMoneySum);
vipMoneySum.setMoneySum(fullMoneySum);
//价格计算
vipMoneySum.sum(order);
//修改库存
int mCount = itemService.modify(order.getNum(), order.getItemId());
//添加订单
int addCount = orderDao.add(order);
return addCount;
}
4、装饰者(Decorator)模式
- 定义:动态的向一个现有的对象添加新的功能,同时又不改变其结构。
- 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
- 缺点:多层装饰比较复杂。
装饰者案例(奶茶制作)
//1.首先新建接口
public interface Tea {
//制作奶茶
void making();
}
//2.实现接口
public class IceTea implements Tea {
@Override
public void making() {
System.out.println("①添加冰块!制作成了一杯冰水!");
}
}
//3.指定被修饰对象,指定被修饰方法
public abstract class DecoratorTea implements Tea {
//被装饰的对象【IceTea】 在指定对象上进行功能扩展
private Tea tea;
public void setTea(Tea tea) {
this.tea = tea;
}
//扩展方法
@Override
public void making(){
//调用被装饰的对象的指定方法
tea.making();
}
}
//4.装饰类1继承实现类DecoratorTea ,为什么不是继承iceTea呢,因为DecoratorTea为基类,iceTea是被装饰者;如果直接继承iceTea就会是死代码,只能装饰iceTea
public class PeachTea extends DecoratorTea {
@Override
public void making() {
super.making();
//扩展
addPeach();
}
//扩展
public void addPeach(){
System.out.println("②制作桃子奶茶!");
}
}
//5.装饰类2继承实现类DecoratorTea
public class CocoTea extends DecoratorTea {
@Override
public void making() {
//执行被装饰的对象指定方法
super.making();
//调用扩展
addCoco();
}
//扩展
public void addCoco(){
System.out.println("③添加椰汁奶茶!");
}
}
//5.把iceTea 装到peachTea 里面,再把peachTea 装到cocoTea ,然后执行cocoTea ,就像套娃一样,执行也会一个套一个执行
public class DecoratorTest {
public static void main(String[] args) {
//创建被装饰的对象
Tea iceTea = new IceTea();
//创建装饰对象
DecoratorTea peachTea = new PeachTea();
//设置被装饰的对象
peachTea.setTea(iceTea);
//执行操作
//peachTea.making();
//创建一个装饰对象
DecoratorTea cocoTea = new CocoTea();
//设置被装饰的对象 peachTea--->Tea???
cocoTea.setTea(peachTea);
//执行
cocoTea.making();
}
}
四、行为型模式
1、观察者(Observer)模式
- 定义
对象之间存在一对多或者一对一依赖,每当一个对象改变状态,依赖他的对象就会收到通知,做出反应。(mq就是一种观察者模式,发布者发布,订阅者获取信息,订阅就能收到。) - 优点
①、观察者与被观察这都是抽象耦合的
②、建立一套触发机制 - 缺点
①、如果被观察者有很多直接、间接的观察者,通知所有的观察者是很耗时间的
②、如果观察者与观察目标有依赖循环,观察目标会触发他们之间进行循环
观察者案例 (Spring观察者模式)
ApplicationContext 事件机制是观察者设计模式的实现,通过 ApplicationEvent 类(ContextRefreshedEvent容器刷新事件)和ApplicationListener 接口,可以实现 ApplicationContext 事件处理。
public class ApplicationContextListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("ContextRefreshedEvent事件,容器内对象发生变更");
}
}
首先,我们实现ApplicationListener对ContextRefreshedEvent事件进行监听
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置文件中配置-->
<bean class="com.itheima.spring.ApplicationContextListener"></bean>
</beans>
在配置文件中配置监听器bean
public class SpringApplicationTest {
/*****
* 1.创建ApplicationContext
* 2.执行相关事件ApplicationEvent:容器初始化完成
* 3.监听事件执行信息ApplicationListener
*/
public static void main(String[] args) {
//1.创建ApplicationContext,会立即加载Bean,加载完毕就相当于执行了事件
ApplicationContext act = new ClassPathXmlApplicationContext("spring-event.xml");
}
}
执行main方法,加载容器发现控制台上打印"ContextRefreshedEvent事件,容器内对象发生变更"
- 原理
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected final Log logger;
private String id;
private String displayName;
@Nullable
private ApplicationContext parent;
@Nullable
private ConfigurableEnvironment environment;
private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors;
private long startupDate;
private final AtomicBoolean active;
private final AtomicBoolean closed;
private final Object startupShutdownMonitor;
@Nullable
private Thread shutdownHook;
private ResourcePatternResolver resourcePatternResolver;
@Nullable
private LifecycleProcessor lifecycleProcessor;
@Nullable
private MessageSource messageSource;
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
//此处会初始化监听器
private final Set<ApplicationListener<?>> applicationListeners;
追踪源码ClassPathXmlApplicationContext,发现在其父类AbstractApplicationContext 中会初始化一个ApplicationListener的集合,那么容器对象是怎么触发ApplicationEvent呢?
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
//注意:此处事件为空则新增ApplicationEvent
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//点击方法进来,发现事件对监听器进行调用唤醒
this.invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
//此时,把ApplicationListener、ApplicationEvent扔进去进行执行
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//执行了ApplicationListener的onApplicationEvent方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, var6);
}
}
}
2、策略(strategy)模式
- 定义:策略模式是对算法的包装,把使用算法的责任和算法本身分隔开,委派给不同的对象管理。策略模式通常把一系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。(被调用者+工厂+策略执行类)
- 优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。 - 缺点:
1、策略类会增多。
2、所有策略类都需要对外暴露。
策略模式案例(商品vip等级价格计算)
购买商品时候,会有不同等级的vip,他们所享受的优惠力度是不一样的,所以需要多个策略针对不同的vip进行价格运算
//1.创建策略接口
public interface Strategy {
/***
* 金额计算
*/
Integer money(Integer money);
}
//2.创建vip1价格策略
@Component(value = "vip1")
public class Vip1 implements Strategy {
/***
* Vip1
* @param money
* @return
*/
@Override
public Integer money(Integer money) {
return money;
}
}
//3.策略2
@Component(value = "vip2")
public class Vip2 implements Strategy {
/***
* Vip1
* @param money
* @return
*/
@Override
public Integer money(Integer money) {
return money-5;
}
}
//4.策略3
@Component(value = "vip3")
public class Vip3 implements Strategy {
/***
* Vip1
* @param money
* @return
*/
@Override
public Integer money(Integer money) {
return 1;
}
}
//5.配置策略映射,省去if判断
#等级和策略实例的映射关系(此处是yml配置文件配置)
strategy:
vipmap:
1: vip1
2: vip2
3: vip3
//6.创建策略工厂
@ConfigurationProperties(prefix = "strategy")
@Component(value = "strategyFactory")
public class StrategyFactory {
/***
* 1.定义一个容器对象(Map)->存储所有策略对象ApplicationContext
* vip1 --->new Vip1()
* vip2 --->new Vip2()
* vip3 --->new Vip3()
*/
@Autowired
private ApplicationContext act;
private Map<Integer,String> vipmap;//把策略装到map中,如: 1: vip1
//2.提供一个方法用于根据等级获取指定策略
public Strategy get(Integer level){
//建立等级和容器中策略的映射关系
//1 - vip1
//2 - vip2
//3 - vip3
String id = vipmap.get(level);
return act.getBean(id,Strategy.class);
}
public void setVipmap(Map<Integer, String> vipmap) {
this.vipmap = vipmap;
}
}
调用时候直接strategyFactory.get(1).money;就可以获取到具体的价格了,参数1代表vip等级。
3、状态(state)模式
- 定义:对有状态的对象,把复杂的“判断逻辑"提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
- 优点:
1、封装了转换规则。
2、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
3、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 - 缺点:
1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式对"开闭原则""的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
状态模式案例(订单状态修改)
在电商案例中,订单状态每次发生变更,都要执行不同的操作,这里正好可以使用状态模式。当订单完成支付的时候,我们需要立即通知商家发货,当订单执行取消的时候,我们需要执行库存回滚,如果订单已支付,还需要执行退款操作,无论是通知商家发货还是执行库存回滚,都是有订单状态决定,因此这里可以使用状态模式来实现。
//1.创建订单对象
public class Context {
private State state;
public Context() {
this.state = null;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
//2..定义订单状态接口
public interface State {
void doAction(Context context);
}
//3.此处定义成功状态处理,处理后将状态改为付款成功状态
public class StartState implements State{
@Override
public void doAction(Context context) {
System.out.println("付款成功!准备发货");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
//4.此处作为退款处理,处理后将状态改为退款成功状态
public class StopState implements State{
@Override
public void doAction(Context context) {
System.out.println("退款成功!准备退款");
context.setState(this);
}
@Override
public String toString() {
return "Stop State";
}
}
//5.此处模拟订单付款、退款业务
public class StateTest {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState().toString());
StopState stopState = new StopState();
stopState.doAction(context);
System.out.println(context.getState().toString());
}
}
执行结果:
Start State
Stop State