热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

1.Spring中所使用的设计模式?

1.Spring中所使用的设计模式?Spring是一个非常优秀的开源框架,项目源码中所使用的设计模式随处可见,这篇文章主要记录一下Spr




1.Spring中所使用的设计模式?

Spring是一个非常优秀的开源框架,项目源码中所使用的设计模式随处可见,这篇文章主要记录一下Spring中常见的设计模式:

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
(2)单例模式:Bean默认为单例模式
(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate
(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。
(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库.

一,工厂模式:

工厂模式是把创建对象的任务交给工厂,从而来降低类与类之间的耦合。Spring最主要的两个特性就是AOP和IOC,其中IOC就是控制反转,将对象的控制权转移给Spring,并由Spring创建实例和管理各个实例之间的依赖关系,其中,对象的创建就是通过BeanFactory 和 ApplicationContext 完成的。

1、首先看BeanFatory的源码:

public interface BeanFactory {
Object getBean(String name) throws BeansException;

T getBean(String name, @Nullable Class requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

T getBean(Class requiredType) throws BeansException;

T getBean(Class requiredType, Object... args) throws BeansException;
//省略...
}

BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。
2、ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还扩展了其他更完整功能,对于Bean创建,ApplicationContext在容器启动时,一次性创建了所有的Bean。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
@Nullable
String getId();

String getApplicationName();

String getDisplayName();

long getStartupDate();

@Nullable
ApplicationContext getParent();

AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

3、BeanFactory 和 ApplicationContext 在哪里初始化Bean就不展开细讲,主要看AbstractApplicationContext类的refresh()方法。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//省略...
try {
//省略...
//初始化所有的单实例 Bean(没有配置赖加载的)
finishBeanFactoryInitialization(beanFactory);
}catch (BeansException ex) {
//省略...
}finally {
//省略...
}
}
}

二、单例模式:

在Spring中的Bean默认的作用域就是singleton单例的。单例模式的好处在于对一些重量级的对象,省略了重复创建对象花费的时间,减少了系统的开销,第二点是使用单例可以减少new操作的次数,减少了GC线程回收内存的压力。

对于单例bean的创建方式,主要看DefaultSingletonBeanRegistry 的 getSingleton() 方法:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 保存单例Objects的缓存集合ConcurrentHashMap,key:beanName --> value:bean实例 */
private final Map singletonObjects &#61; new ConcurrentHashMap<>(256);

public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//检查缓存中是否有实例&#xff0c;如果缓存中有实例&#xff0c;直接返回
Object singletonObject &#61; this.singletonObjects.get(beanName);
if (singletonObject &#61;&#61; null) {
//省略...
try {
//通过singletonFactory获取单例
singletonObject &#61; singletonFactory.getObject();
newSingleton &#61; true;
}
//省略...
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
//返回实例
return singletonObject;
}
}

protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

从源码中可以看出&#xff0c;是通过ConcurrentHashMap的方式&#xff0c;如果在Map中存在则直接返回&#xff0c;如果不存在则创建&#xff0c;并且put进Map集合中&#xff0c;并且整段逻辑是使用同步代码块包住的&#xff0c;所以是线程安全的。


三、策略模式&#xff1a;

策略模式&#xff0c;简单来说就是封装好一组策略算法&#xff0c;外部客户端根据不同的条件选择不同的策略算法解决问题。比如在Spring的Resource类&#xff0c;针对不同的资源&#xff0c;Spring定义了不同的Resource类的实现类&#xff0c;以此实现不同的访问方式。我们看一张类图&#xff1a;img

简单介绍一下Resource的实现类&#xff1a;

UrlResource&#xff1a;访问网络资源的实现类。
ServletContextResource&#xff1a;访问相对于 ServletContext 路径里的资源的实现类。
ByteArrayResource&#xff1a;访问字节数组资源的实现类。
PathResource&#xff1a;访问文件路径资源的实现类。
ClassPathResource&#xff1a;访问类加载路径里资源的实现类。
写一段伪代码来示范一下Resource类的使用&#xff1a;

&#64;RequestMapping(value &#61; "/resource", method &#61; RequestMethod.GET)
public String resource(&#64;RequestParam(name &#61; "type") String type,
&#64;RequestParam(name &#61; "arg") String arg) throws Exception {
Resource resource;
//这里可以优化为通过工厂模式&#xff0c;根据type创建Resource的实现类
if ("classpath".equals(type)) {
//classpath下的资源
resource &#61; new ClassPathResource(arg);
} else if ("file".equals(type)) {
//本地文件系统的资源
resource &#61; new PathResource(arg);
} else if ("url".equals(type)) {
//网络资源
resource &#61; new UrlResource(arg);
} else {
return "fail";
}
InputStream is &#61; resource.getInputStream();
ByteArrayOutputStream os &#61; new ByteArrayOutputStream();
int i;
while ((i &#61; is.read()) !&#61; -1) {
os.write(i);
}
String result &#61; new String(os.toByteArray(), StandardCharsets.UTF_8);
is.close();
os.close();
return "type:" &#43; type &#43; ",arg:" &#43; arg &#43; "\r\n" &#43; result;
}

这就是策略模式的思想&#xff0c;通过外部条件使用不同的算法解决问题。其实很简单&#xff0c;因为每个实现类的getInputStream()方法都不一样&#xff0c;我们看ClassPathResource的源码&#xff0c;是通过类加载器加载资源&#xff1a;

public class ClassPathResource extends AbstractFileResolvingResource {

private final String path;

&#64;Nullable
private ClassLoader classLoader;

&#64;Nullable
private Class clazz;

&#64;Override
public InputStream getInputStream() throws IOException {
InputStream is;
//通过类加载器加载类路径下的资源
if (this.clazz !&#61; null) {
is &#61; this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader !&#61; null) {
is &#61; this.classLoader.getResourceAsStream(this.path);
}
else {
is &#61; ClassLoader.getSystemResourceAsStream(this.path);
}
//如果输入流is为null&#xff0c;则报错
if (is &#61;&#61; null) {
throw new FileNotFoundException(getDescription() &#43; " cannot be opened because it does not exist");
}
//返回InputStream
return is;
}
}

再看UrlResource的源码&#xff0c;获取InputStream的实现又是另一种策略。

public class UrlResource extends AbstractFileResolvingResource {
&#64;Nullable
private final URI uri;

private final URL url;

private final URL cleanedUrl;

&#64;Override
public InputStream getInputStream() throws IOException {
//获取连接
URLConnection con &#61; this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
//获取输入流&#xff0c;并返回
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
}

四、代理模式&#xff1a;

AOP是Spring的一个核心特性(面向切面编程)&#xff0c;作为面向对象的一种补充&#xff0c;用于将那些与业务无关&#xff0c;但却对多个对象产生影响的公共行为和逻辑&#xff0c;抽取并封装为一个可重用的模块&#xff0c;减少系统中的重复代码&#xff0c;降低了模块间的耦合度&#xff0c;提高系统的可维护性。可用于权限认证、日志、事务处理。

Spring AOP实现的关键在于动态代理&#xff0c;主要有两种方式&#xff0c;JDK动态代理和CGLIB动态代理&#xff1a;

&#xff08;1&#xff09;JDK动态代理只提供接口的代理&#xff0c;不支持类的代理&#xff0c;要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类&#xff0c;在获取代理对象时&#xff0c;使用Proxy类来动态创建目标类的代理类&#xff08;即最终真正的代理类&#xff0c;这个类继承自Proxy并实现了我们定义的接口&#xff09;&#xff0c;当代理对象调用真实对象的方法时&#xff0c; InvocationHandler 通过invoke()方法反射来调用目标类中的代码&#xff0c;动态地将横切逻辑和业务编织在一起&#xff1b;

&#xff08;2&#xff09;如果被代理类没有实现接口&#xff0c;那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB&#xff08;Code Generation Library&#xff09;&#xff0c;是一个代码生成的类库&#xff0c;可以在运行时动态的生成指定类的一个子类对象&#xff0c;并覆盖其中特定方法并添加增强代码&#xff0c;从而实现AOP。CGLIB是通过继承的方式做的动态代理&#xff0c;因此如果某个类被标记为final&#xff0c;那么它是无法使用CGLIB做动态代理的。

我们看DefaultAopProxyFactory的createAopProxy()方法&#xff0c;Spring通过此方法创建动态代理类&#xff1a;

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
&#64;Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass &#61; config.getTargetClass();
if (targetClass &#61;&#61; null) {
throw new AopConfigException("TargetSource cannot determine target class: " &#43; "Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}

从源码中可以看出&#xff0c;Spring会先判断是否实现了接口&#xff0c;如果实现了接口就使用JDK动态代理&#xff0c;如果没有实现接口则使用Cglib动态代理&#xff0c;也可以通过配置&#xff0c;强制使用Cglib动态代理&#xff0c;配置如下&#xff1a;


JDK动态代理和Cglib动态代理的区别&#xff1a;

JDK动态代理只能对实现了接口的类生成代理&#xff0c;没有实现接口的类不能使用。
Cglib动态代理即使被代理的类没有实现接口&#xff0c;也可以使用&#xff0c;因为Cglib动态代理是使用继承被代理类的方式进行扩展。
Cglib动态代理是通过继承的方式&#xff0c;覆盖被代理类的方法来进行代理&#xff0c;所以如果方法是被final修饰的话&#xff0c;就不能进行代理。


五、模板模式&#xff1a;

所谓模板就是一个方法&#xff0c;这个方法定义了算法的骨架&#xff0c;即将算法的实现定义成了一组步骤&#xff0c;并将一些步骤延迟到子类中实现&#xff0c;子类重写抽象类中的模板方法实现算法骨架中特定的步骤。模板模式可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。在模板方法模式中&#xff0c;我们可以将相同部分的代码放在父类中&#xff0c;而将不同的代码放入不同的子类中&#xff0c;从而解决代码重复的问题。

Spring中的事务管理器就运用模板模式的设计&#xff0c;首先看PlatformTransactionManager类。这是最底层的接口&#xff0c;定义提交和回滚的方法。

public interface PlatformTransactionManager {
TransactionStatus getTransaction(&#64;Nullable TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;
}

毫无意外&#xff0c;使用了抽象类作为骨架&#xff0c;接着看AbstractPlatformTransactionManager类。

&#64;Override
public final void commit(TransactionStatus status) throws TransactionException {
//省略...
DefaultTransactionStatus defStatus &#61; (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, false);
return;
}

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, true);
return;
}
//调用processCommit()
processCommit(defStatus);
}

//这个方法定义了骨架&#xff0c;里面会调用一个doRollback()的模板方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//调用doRollback()模板方法
doRollback(status);
}
else {
//省略...
}
//省略了很多代码...
}

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
//省略...
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//省略...
//调用doCommit()模板方法
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback &#61; status.isGlobalRollbackOnly();
}
//省略了很多代码...
}

//模板方法doRollback()&#xff0c;把重要的步骤延迟到子类去实现
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;

//模板方法doCommit()&#xff0c;把重要的步骤延迟到子类去实现
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;

模板方法则由各种事务管理器的实现类去实现&#xff0c;也就是把骨架中重要的doRollback()延迟到子类。一般来说&#xff0c;Spring默认是使用的事务管理器的实现类是DataSourceTransactionManager。

//通过继承AbstractPlatformTransactionManager抽象类
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
//重写doCommit()方法&#xff0c;实现具体commit的逻辑
&#64;Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject &#61; (DataSourceTransactionObject) status.getTransaction();
Connection con &#61; txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" &#43; con &#43; "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}

//重写doRollback()方法&#xff0c;实现具体的rollback的逻辑
&#64;Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject &#61; (DataSourceTransactionObject) status.getTransaction();
Connection con &#61; txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" &#43; con &#43; "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
}

如果你是用Hibernate框架&#xff0c;Hibernate也有自身的实现&#xff0c;这就体现了设计模式的开闭原则&#xff0c;通过继承或者组合的方式进行扩展&#xff0c;而不是直接修改类的代码。Hibernate的事务管理器则是HibernateTransactionManager。

public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {

//重写doCommit()方法&#xff0c;实现Hibernate的具体commit的逻辑
&#64;Override
protected void doCommit(DefaultTransactionStatus status) {HibernateTransactionObject txObject &#61; (HibernateTransactionObject) status.getTransaction();
Transaction hibTx &#61; txObject.getSessionHolder().getTransaction();
Assert.state(hibTx !&#61; null, "No Hibernate transaction");
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" &#43;
txObject.getSessionHolder().getSession() &#43; "]");
}
try {
hibTx.commit();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
//省略...
}

//重写doRollback()方法&#xff0c;实现Hibernate的具体rollback的逻辑
&#64;Override
protected void doRollback(DefaultTransactionStatus status) {
HibernateTransactionObject txObject &#61; (HibernateTransactionObject) status.getTransaction();
Transaction hibTx &#61; txObject.getSessionHolder().getTransaction();
Assert.state(hibTx !&#61; null, "No Hibernate transaction");
//省略...
try {
hibTx.rollback();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
//省略...
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
txObject.getSessionHolder().getSession().clear();
}
}
}
}

其实模板模式在日常开发中也经常用&#xff0c;比如一个方法中&#xff0c;前后代码都一样&#xff0c;只有中间有一部分操作不同&#xff0c;就可以使用模板模式进行优化代码&#xff0c;这可以大大地减少冗余的代码&#xff0c;非常实用。


六、适配器模式与责任链模式&#xff1a;

适配器模式能使接口不兼容的对象能够相互合作&#xff0c;将一个类的接口&#xff0c;转换成客户期望的另外一个接口。

在SpringAOP中有一个很重要的功能就是使用的 Advice&#xff08;通知&#xff09; 来增强被代理类的功能&#xff0c;Advice主要有MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice这几种。每个Advice都有对应的拦截器&#xff0c;如下所示&#xff1a;
img

Spring需要将每个 Advice 都封装成对应的拦截器类型返回给容器&#xff0c;所以需要使用适配器模式对 Advice 进行转换。对应的就有三个适配器&#xff0c;我们看个类图&#xff1a;

img

适配器在Spring中是怎么把通知类和拦截类进行转换的呢&#xff0c;我们先看适配器的接口。定义了两个方法&#xff0c;分别是supportsAdvice()和getInterceptor()。

public interface AdvisorAdapter {
//判断通知类是否匹配
boolean supportsAdvice(Advice advice);
//传入通知类&#xff0c;返回对应的拦截类
MethodInterceptor getInterceptor(Advisor advisor);
}

其实很简单&#xff0c;可以看出转换的方法就是getInterceptor()&#xff0c;通过supportsAdvice()进行判断。我们看前置通知的适配器的实现类MethodBeforeAdviceAdapter。

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
//判断是否匹配MethodBeforeAdvice通知类
&#64;Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
//传入MethodBeforeAdvice&#xff0c;转换为MethodBeforeAdviceInterceptor拦截类
&#64;Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice &#61; (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}

getInterceptor()方法中&#xff0c;调用了对应的拦截类的构造器创建对应的拦截器返回&#xff0c;传入通知类advice作为参数。接着我们看拦截器MethodBeforeAdviceInterceptor。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
//成员变量&#xff0c;通知类
private MethodBeforeAdvice advice;

//定义了有参构造器&#xff0c;外部通过有参构造器创建MethodBeforeAdviceInterceptor
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice &#61; advice;
}

//当调用拦截器的invoke方法时&#xff0c;就调用通知类的before()方法&#xff0c;实现前置通知
&#64;Override
public Object invoke(MethodInvocation mi) throws Throwable {
//调用通知类的before()方法&#xff0c;实现前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}

}

那么在哪里初始化这些适配器呢&#xff0c;我们看DefaultAdvisorAdapterRegistry()

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

private final List adapters &#61; new ArrayList<>(3);

public DefaultAdvisorAdapterRegistry() {
//初始化适配器&#xff0c;添加到adapters集合&#xff0c;也就是注册
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

&#64;Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}

//获取所有的拦截器
&#64;Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List interceptors &#61; new ArrayList<>(3);
Advice advice &#61; advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//遍历adapters集合
for (AdvisorAdapter adapter : this.adapters) {
//调用supportsAdvice()方法&#xff0c;判断入参的advisor是否有匹配的适配器
if (adapter.supportsAdvice(advice)) {
//如果匹配&#xff0c;则调用getInterceptor()转换成对应的拦截器&#xff0c;添加到interceptors集合中
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
//返回拦截器集合
return interceptors.toArray(new MethodInterceptor[0]);
}

}

适配器模式在这里就是把通知类转为拦截类&#xff0c;转为拦截类之后&#xff0c;就添加到拦截器集合中。添加到拦截器集合之后&#xff0c;就用到了责任链模式&#xff0c;在ReflectiveMethodInvocation类被调用&#xff0c;我们看JDK动态代理JdkDynamicAopProxy的invoke()方法。

&#64;Override
&#64;Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
//这里就是获取拦截器集合&#xff0c;最后就会调用到上文说的getInterceptors()
List chain &#61; this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//省略...
}else {
//创建一个MethodInvocation
invocation &#61; new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//调用proceed()方法&#xff0c;底层会通过指针遍历拦截器集合&#xff0c;然后实现前置通知等功能
retVal &#61; invocation.proceed();
}
//省略...
}

最后就在ReflectiveMethodInvocation里调用proceed()方法&#xff0c;proceed()方法是一个递归的方法&#xff0c;通过指针控制递归的结束。这是很典型的责任链模式。

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
protected final List interceptorsAndDynamicMethodMatchers;
//指针
private int currentInterceptorIndex &#61; -1;

protected ReflectiveMethodInvocation(Object proxy, &#64;Nullable Object target, Method method, &#64;Nullable Object[] arguments, &#64;Nullable Class targetClass, List interceptorsAndDynamicMethodMatchers) {
//省略...
//拦截器的集合
this.interceptorsAndDynamicMethodMatchers &#61; interceptorsAndDynamicMethodMatchers;
}

&#64;Override
&#64;Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex &#61;&#61; this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//递归结束
return invokeJoinpoint();
}
//获取拦截器&#xff0c;并且当前的指针&#43;1
Object interceptorOrInterceptionAdvice &#61; this.interceptorsAndDynamicMethodMatchers.get(&#43;&#43;this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm &#61;
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//匹配失败&#xff0c;跳过&#xff0c;递归下一个
return proceed();
}
}
else {
//匹配拦截器&#xff0c;强转为拦截器&#xff0c;然后执行invoke()方法&#xff0c;然后就会调用拦截器里的成员变量的before()&#xff0c;afterReturning()等等&#xff0c;实现前置通知&#xff0c;后置通知&#xff0c;异常通知
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}

这里可能没学过责任链模式的同学会看得有点晕&#xff0c;但是学过责任链模式应该很容易看懂&#xff0c;这其实跟SpringMVC的拦截器的逻辑实现几乎一样的。


七 、观察者模式&#xff1a;

观察者模式是一种对象行为型模式&#xff0c;当一个对象发生变化时&#xff0c;这个对象所依赖的对象也会做出反应。

Spring 事件驱动模型就是观察者模式很经典的一个应用。

1、事件角色&#xff1a;

在Spring事件驱动模型中&#xff0c;首先有事件角色ApplicationEvent&#xff0c;这是一个抽象类&#xff0c;抽象类下有四个实现类代表四种事件。

ContextStartedEvent&#xff1a;ApplicationContext启动后触发的事件。
ContextStoppedEvent&#xff1a;ApplicationContext停止后触发的事件。
ContextRefreshedEvent&#xff1a;ApplicationContext初始化或刷新完成后触发的事件。
ContextClosedEvent&#xff1a;ApplicationContext关闭后触发的事件。
img

2、事件发布者&#xff1a;

有了事件之后&#xff0c;需要有个发布者发布事件&#xff0c;发布者对应的类是ApplicationEventPublisher。

&#64;FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}

void publishEvent(Object event);
}

&#64;FunctionalInterface表示这是一个函数式接口&#xff0c;函数式接口只有一个抽象方法。ApplicationContext类又继承了ApplicationEventPublisher类&#xff0c;所以我们可以使用ApplicationContext发布事件。

3、事件监听者&#xff1a;

发布事件后需要有事件的监听者&#xff0c;事件监听者通过实现接口ApplicationListener来定义&#xff0c;这是一个函数式接口&#xff0c;并且带有泛型&#xff0c;要求E参数是ApplicationEvent的子类。

&#64;FunctionalInterface
public interface ApplicationListener extends EventListener {

void onApplicationEvent(E event);
}

4、下面我们演示一下怎么使用&#xff0c;首先继承抽象类ApplicationEvent定义一个事件角色PayApplicationEvent。

public class PayApplicationEvent extends ApplicationEvent {

private String message;

public PayApplicationEvent(Object source, String message) {
super(source);
this.message &#61; message;
}

public String getMessage() {
return message;
}
}

接着定义一个PayApplicationEvent事件的监听者PayListener。

&#64;Component
public class PayListener implements ApplicationListener {

&#64;Override
public void onApplicationEvent(PayApplicationEvent event) {
String message &#61; event.getMessage();
System.out.println("监听到PayApplicationEvent事件&#xff0c;消息为&#xff1a;" &#43; message);
}
}

最后我们使用ApplicationContext发布事件。

&#64;SpringBootApplication
public class SpringmvcApplication {

public static void main(String[] args) throws Exception {
ApplicationContext applicationContext &#61; SpringApplication.run(SpringmvcApplication.class, args);
applicationContext.publishEvent(new PayApplicationEvent(applicationContext,"成功支付100元&#xff01;"));
}

}

启动之后我们可以看到控制台打印&#xff1a;

img







推荐阅读
  • 本文详细介绍了Oracle RMAN中的增量备份机制,重点解析了差异增量和累积增量备份的概念及其在不同Oracle版本中的实现。通过对比两种备份方式的特点,帮助读者选择合适的备份策略。 ... [详细]
  • 本文探讨了一个Web工程项目的需求,即允许用户随时添加定时任务,并通过Quartz框架实现这些任务的自动化调度。文章将介绍如何设计任务表以存储任务信息和执行周期,以及如何通过一个定期扫描机制自动识别并加载新任务到调度系统中。 ... [详细]
  • 构建Python自助式数据查询系统
    在现代数据密集型环境中,业务团队频繁需要从数据库中提取特定信息。为了提高效率并减少IT部门的工作负担,本文探讨了一种利用Python语言实现的自助数据查询工具的设计与实现。 ... [详细]
  • 深入解析Nacos服务自动注册机制
    本文将探讨Nacos服务自动注册的具体实现方法,特别是如何通过Spring事件机制完成服务注册。通过对Nacos源码的详细分析,帮助读者理解其背后的原理。 ... [详细]
  • SpringBoot底层注解用法及原理
    2.1、组件添加1、Configuration基本使用Full模式与Lite模式示例最佳实战配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断配置类组 ... [详细]
  • 使用 ModelAttribute 实现页面数据自动填充
    本文介绍了如何利用 Spring MVC 中的 ModelAttribute 注解,在页面跳转后自动填充表单数据。主要探讨了两种实现方法及其背后的原理。 ... [详细]
  • 本文探讨了如何在 Spring MVC 框架下,通过自定义注解和拦截器机制来实现细粒度的权限管理功能。 ... [详细]
  • Maven + Spring + MyBatis + MySQL 环境搭建与实例解析
    本文详细介绍如何使用MySQL数据库进行环境搭建,包括创建数据库表并插入示例数据。随后,逐步指导如何配置Maven项目,整合Spring框架与MyBatis,实现高效的数据访问。 ... [详细]
  • 本文详细介绍了在 Windows 7 上安装和配置 PHP 5.4 的 Memcached 分布式缓存系统的方法,旨在减少数据库的频繁访问,提高应用程序的响应速度。 ... [详细]
  • 本文介绍了如何使用C# Winform开发局域网内的文件传输功能,详细描述了从用户界面到后端网络通信的具体实现。 ... [详细]
  • 优雅地记录API调用时长
    本文旨在探讨如何高效且优雅地记录API接口的调用时长,通过实际案例和代码示例,帮助开发者理解并实施这一技术,提高系统的可观测性和调试效率。 ... [详细]
  • 本文详细介绍了如何在Vue项目中集成和配置XGPlayer视频插件,包括安装步骤、基本配置以及常见问题的解决方法。 ... [详细]
  • 华为云openEuler环境下的Web应用部署实践
    本文详细记录了在华为云openEuler系统上进行Web应用部署的具体步骤,包括配置yum源、安装Apache、MariaDB、PHP及其相关组件,并完成WordPress的安装与配置过程。 ... [详细]
  • 本文详细介绍了如何处理Oracle数据库中的ORA-00227错误,即控制文件中检测到损坏块的问题,并提供了具体的解决方案。 ... [详细]
  • 利用Cookie实现用户登录状态的持久化
    本文探讨了如何使用Cookie技术在Web应用中实现用户登录状态的持久化,包括Cookie的基本概念、优势及主要操作方法,并通过一个简单的Java Web项目示例展示了具体实现过程。 ... [详细]
author-avatar
gbn3312168
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有