Spring的9种设计模式(二)

此文为下篇,上篇文章链接

六、代理(Proxy

为其他对象提供一种代理以控制对这个对象的访问。  从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。 
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxyCglib2AopProxy。 

代理类

Spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。 

七、观察者(Observer

7.1 观察者模式概览

定义: 在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

模式结构图:

观察者模式结构图

模式包含角色 :

观察者模式由四个角色组成:

抽象主题角色(Subject):把所有的观察者角色的引用保存在一个集合中,可以有任意数量的观察者。其提供一个接口,可以添加、删除观察者,并可以向登记过的观察者发送通知。

具体主题角色(Observer):实现了抽象主题角色提供的接口的一个具体类。

抽象观察者角色(ConcreteSubject):提供一个接口,以接收主题角色发送的通知。

具体观察者角色(ConcreteObserver):实现了抽象观察者角色提供的接口的一个具体类。

使用场景 :

监听器、日志收集、短信通知、邮件通知

7.2 观察者模式实现

1.定义通知回调格式

@Data
@Builder
public class Event {
    /**
     * 事件源
     */
    private Object source;
    /**
     * 通知目标
     */
    private Object target;
    /**
     * 监听事件
     */
    private String message;
    /**
     * 事件时间
     */
    private long time;
}

2.定义观察者接口

public interface Observer {
    /**
     * 被观察的操作方法
     * @param event 操作打印日志
     */
    public void operation(Event event);
}

3. 定义监听接口

public interface Listener {
    /**
     * 注册观察者
     * @param object
     */
    public void registerObserver(Observer object);
    /**
     * 删除观察者
     * @param observer
     */
    public void removeObserver(Observer observer);
    /**
     * 通知观察者
     * @param message 通知消息
     */
    public void notifyObserver(String message);
}

4.实现观察者接口

public class Person implements Observer {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void operation(Event event) {
        System.out.println(" 观察者 : " + name + " 接收到信息 " + event.toString());
    }
}

5.实现监听者接口

public class EventListener implements Listener {
    private List<Observer> events = new ArrayList<Observer>();

    public void registerObserver(Observer object) {
        events.add(object);
    }

    public void removeObserver(Observer observer) {
        events.remove(observer);
    }

    public void notifyObserver(String message) {
        for (Observer observer : events) {
            Event event = Event.builder()
                    .source(this)
                    .target(observer)
                    .message(message)
                    .time(System.currentTimeMillis())
                    .build();
            observer.operation(event);
        }
    }
}

6.实现观察者与监听者关联

public class Subject  {
    Listener listener;

    public Subject(Listener listener) {
        this.listener = listener;
    }

    public void sendMsg(String msg){
        System.out.println(msg);
        listener.notifyObserver(msg);
    }

    public void sendBlog(String blog){
        System.out.println(blog);
        listener.notifyObserver(blog);
    }
}

7.测试

public class ObserverTest {

    @Test
    public void test_observer(){
        Person person = new Person("张三");
        Person person1 = new Person("李四");
        Person person2 = new Person("王五");

        Listener listener = new EventListener();
        listener.registerObserver(person);
        listener.registerObserver(person1);
        listener.registerObserver(person2);

        Subject subject = new Subject(listener);
        subject.sendMsg("官宣");

        //李四取消关注
        listener.removeObserver(person1);
        System.out.println("李四取消关注\n");
        
        subject.sendBlog("博客 - 观察者模式");
    }
}

 运行结果:

7.3 spring观察者模式源码

7.3.1 jdk事件监听事件

 java中的事件机制的参与者有3种角色

  • Event Eource:事件源,发起事件的主体。
  • Event Object:事件状态对象,传递的信息载体,就好比Watcher的update方法的参数,可以是事件源本身,一般作为参数存在于listerner 的方法之中。
  • Event Listener:事件监听器,当它监听到event object产生的时候,它就调用相应的方法,进行处理。

7.3.2 Spring事件监听机制

Spring的事件驱动模型由三部分组成:

  • 事件:ApplicationEvent,继承自JDK的EventObject,所有事件将继承它,并通过source得到事件源。
  • 事件发布者:ApplicationEventPublisher及ApplicationEventMulticaster接口,使用这个接口,我们的Service就拥有了发布事件的能力。
  • 事件订阅者:ApplicationListener,继承自JDK的EventListener,所有监听器将继承它

 

1.spring事件:ApplicationEvent,该抽象类继承了EventObject类,jdk建议所有的事件都应该继承自EventObject。

public abstract class ApplicationEvent extends EventObject {、
	/** use serialVersionUID from Spring 1.2 for interoperability */
	private static final long serialVersionUID = 7099057708183571937L;
	/** System time when the event happened */
	private final long timestamp;

	/**
	 * Create a new ApplicationEvent.
	 * @param source the object on which the event initially occurred (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	/**
	 * Return the system time in milliseconds when the event happened.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}
}

2.spring事件监听器:ApplicationListener,该接口继承了EventListener接口,jdk建议所有的事件监听器都应该继承EventListener。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

3.spring事件发布:ApplicationEventPublisher 。 ApplicationContext继承了该接口,在ApplicationContext的抽象类AbstractApplicationContext中做了实现。

public interface ApplicationEventPublisher {
	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an application event. Events may be framework events
	 * (such as RequestHandledEvent) or application-specific events.
	 * @param event the event to publish
	 * @see org.springframework.web.context.support.RequestHandledEvent
	 */
	void publishEvent(ApplicationEvent event);

	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an event.
	 * <p>If the specified {@code event} is not an {@link ApplicationEvent},
	 * it is wrapped in a {@link PayloadApplicationEvent}.
	 * @param event the event to publish
	 * @since 4.2
	 * @see PayloadApplicationEvent
	 */
	void publishEvent(Object event);

}

AbstractApplicationContext类中publishEvent方法实现: 

@Override
	public void publishEvent(ApplicationEvent event) {
		publishEvent(event, null);
	}

	/**
	 * Publish the given event to all listeners.
	 * <p>Note: Listeners get initialized after the MessageSource, to be able
	 * to access it within listener implementations. Thus, MessageSource
	 * implementations cannot publish events.
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 */
	@Override
	public void publishEvent(Object event) {
		publishEvent(event, null);
	}

	/**
	 * Publish the given event to all listeners.
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 * @param eventType the resolved event type, if known
	 * @since 4.2
	 */
	protected void publishEvent(Object event, ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in " + getDisplayName() + ": " + event);
		}

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<Object>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

  由上代码可知,AbstractApplicationContext类并没有具体的做事件广播,而是委托给ApplicationEventMulticaster来进行,ApplicationEventMulticaster的multicastEvent()方法实现如下:

SimpleApplicationEventMulticaster#multicastEvent:

@Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
@SuppressWarnings({"unchecked", "rawtypes"})
	protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				listener.onApplicationEvent(event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			try {
				listener.onApplicationEvent(event);
			}
			catch (ClassCastException ex) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				LogFactory.getLog(getClass()).debug("Non-matching event type for listener: " + listener, ex);
			}
		}
	}

 获得listener集合,遍历listener触发事件Executor接口有多个实现类,可以支持同步或异步广播事件。

7.2参考文章:Spring中的设计模式--观察者模式

 

八、策略(Strategy

定义:策略本身就是为了实现某一个目标而采取的一种工作方式,因此只要能够达成目标,则采取哪一种策略都可以;因此多种实际的策略之间是相互平行的。

用自己的话:实现一个功能,可以有多种不同的方式,我们可以根据不同的条件确定不同的方式去实现这个功能。例如:比如支付方式的选择,可以用银联、微信、支付宝,根据不同的入参,确定不同的支付方式,走不同的逻辑。

8.1 spring中的策略模式

在Spring中,实例化对象的时候用到了Strategy模式,图示如下所示:

在上图中:

InstantiationStrategy为抽象接口,SimpleInstantiationStrategy实现接口,在instantiate方法中进行判断:

1.针对bd中没有MethodOverrides的,直接通过jdk反射进行构造函数调用;
2.而针对有MethodOverrides的,则可以通过另一种方式处理,在SimpleInstantiationStrategy中是通过:instantiateWithMethodInjection()方法处理的,在CglibSubclassingInstantiationStrategy中对该方法做了override实现。CglibSubclassingInstantiationStrategy继承自SimpleInstantiationStrategy。

 接下来,首先看SimpleInstantiationStrategy#instantiate

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        //1.没有methodOverrides,jdk反射
        if (bd.getMethodOverrides().isEmpty()) {
            Constructor constructorToUse;
            synchronized(bd.constructorArgumentLock) {
                constructorToUse = (Constructor)bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = (Constructor)AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[])null);
                                }
                            });
                        } else {
                            constructorToUse = clazz.getDeclaredConstructor((Class[])null);
                        }

                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    } catch (Throwable var9) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", var9);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse, new Object[0]);
        } else {
            //2.instantiateWithMethodInjection()方法处理
            return this.instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

 CglibSubclassingInstantiationStrategy重写的instantiateWithMethodInjection

protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner, Constructor<?> ctor, Object... args) {
        return (new CglibSubclassingInstantiationStrategy.CglibSubclassCreator(bd, owner)).instantiate(ctor, args);
    }

 看代码的时候,我们应该就有疑问:什么情况下有methodOverrides,什么情况下没有methodOverrides?

spring有两个标签参数会产生MethodOverrides ,分别是 lookup-method,replaced-method 。
当有这两个标签的时候MethodOverrides不为空

参考文章:spring实例化二:SimpleInstantiationStrategy

【Spring】 - lookup-method和replaced-method使用

九、模板方法(Template Method

定义:一个操作中的算法的骨架, 而将一些步骤延迟到子类中。 Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
应用场景: JdbcTemplate等。

模板模式uml图:

9.1 模板方法实例

实例简介:
模拟试下了一个jdbcTemplate模板类,比如从数据源中获取一个链接,然后创建语句集,执行语句集获取结果集,解析结果集,最后结果集、语句集、连接的关闭都是固定的流程,而一个对象的如何映射成一个对象是需要具体的实现,这个由具体调用类实现对象的映射,这个步骤是可以重新定义的

接下来就是手写一个简单的jdbcTemplate的实现

行的映射接口,作用把数据库一行映射为一个对象

/**
 * @Project: spring
 * @description:   行的映射接口 ,把数据库一行映射为一个对象
 * @author: sunkang
 * @create: 2018-09-03 14:02
 * @ModificationHistory who      when       What
 **/
public interface RowMapper<T> {
    T  mapRow(ResultSet rs,int rownum) throws  Exception;
}

jdbcTemplate 的具体实现类

/**
 * @Project: spring
 * @description:    jdbc 的模板类
 * @author: sunkang
 * @create: 2018-09-03 14:06
 * @ModificationHistory who      when       What
 **/
public class JdbcTemplate {

    private DataSource dataSource ;

    public JdbcTemplate(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    /**
     * 得到连接
     * @return
     * @throws SQLException
     */
    private Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }

    /**
     * 获得标准的语句集
     * @param sql
     * @param connection
     * @return
     * @throws SQLException
     */
    private PreparedStatement getPreparedStatement(String sql,Connection connection) throws SQLException {
        return connection.prepareStatement(sql);
    }

    /**
     * 得到结果集
     * @param preparedStatement
     * @param values
     * @return
     * @throws SQLException
     */
    private ResultSet getResultSet(PreparedStatement preparedStatement,Object[] values) throws SQLException {
       int i=0;
        for (Object value: values){
            preparedStatement.setObject(i++,value);
        }
        return preparedStatement.getResultSet();
    }
    /**
     * 解析结果集
     * @param resultSet
     * @param rowMapper
     * @return
     * @throws Exception
     */
    private List<?>  parsedResultSet(ResultSet resultSet,RowMapper<?> rowMapper) throws Exception {
        int rownumber = 1;
        List<Object> list = new ArrayList<Object>();
        while (resultSet.next()){
            list.add (rowMapper.mapRow(resultSet,rownumber++));
        }
        return  list;
    }
    /**
     * 关闭结果集
     * @param rs
     * @throws SQLException
     */
    private void  closeResultSet(ResultSet rs) throws SQLException {
        rs.close();
    }
    /**
     * 关闭语句集
     * @param statement
     * @throws SQLException
     */
    private  void closePreparedStatement(PreparedStatement statement ) throws SQLException {
        statement.close();
    }
    /**
     * 关闭连接
     * @param connection
     * @throws SQLException
     */
    private  void closeConnect(Connection connection ) throws SQLException {
        //通常把它放到连接池回收
        connection.close();
    }
    public List<?> executeQuery(String sql , RowMapper<?> mapper , Object[] values){
        try {
            //1.获得连接
           Connection connection =  this.getConnection();
            //2.获取语句集
            PreparedStatement  pstm = this.getPreparedStatement(sql,connection);
            //3.执行语句集,获得结果集
            ResultSet rs= this.getResultSet(pstm,values);
            //4.解析结果集
            List<?> list  = this.parsedResultSet(rs,mapper);
            //5.关闭结果集
            this.closeResultSet(rs);
            //6.关闭语句集
            this.closePreparedStatement(pstm);
            //7.关闭连接
            this.closeConnect(connection);
            return  list;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

模拟需要查询的实体类 Merber

/**
 * @Project: spring
 * @description:  Merber bean对象
 * @author: sunkang
 * @create: 2018-09-03 14:04
 * @ModificationHistory who      when       What
 **/
public class Merber {
    private String userName;
    private String password;
    private String nickName;

    private  int age;
    private  String addr;


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

模拟Merber的dao层

/**
 * @Project: spring
 * @description:   模拟Merber的dao层
 * @author: sunkang
 * @create: 2018-09-03 16:34
 * @ModificationHistory who      when       What
 **/
public class MerberDao {
    //为什么不继承,主要是为了解耦
   //这里没有传直接的数据源 ,这里可以是mysql或者oracle的数据源都可以
    private JdbcTemplate jdbcTemplate = new JdbcTemplate(null);
    public List<?> query(){
        String sql = "select * from t_member";

        return jdbcTemplate.executeQuery(sql, new RowMapper<Merber>() {
            @Override
            public Merber mapRow(ResultSet rs, int rownum) throws Exception {
                Merber merber = new Merber();
                merber.setUserName(rs.getString("username"));
                merber.setPassword(rs.getString("password"));
                merber.setNickName(rs.getString("nickname"));
                merber.setAge(rs.getInt("age"));
                merber.setAddr(rs.getString("addr"));
                return merber;
            }
        },null);
    }
}

模板方法测试

/**
 * @Project: spring
 * @description: 模板方法测试
 * @author: sunkang
 * @create: 2018-09-03 16:39
 * @ModificationHistory who      when       What
 **/
public class TemplateTest {

    public static void main(String[] args) {
        MerberDao merberDao   = new MerberDao();
        merberDao.query();
    }
}

9.2 spring中JdbcTemplate应有模板模式

spring中的JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。这可能是Template Method不需要继承的另一种实现方式吧。 

以下是一个具体的例子: 
JdbcTemplate中的execute方法 

 


JdbcTemplate执行execute方法 

更详细的,请参考模板模式之spring的jdbcTemplate

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值