热门标签 | 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







推荐阅读
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 本文深入探讨了SQL数据库中常见的面试问题,包括如何获取自增字段的当前值、防止SQL注入的方法、游标的作用与使用、索引的形式及其优缺点,以及事务和存储过程的概念。通过详细的解答和示例,帮助读者更好地理解和应对这些技术问题。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • Kubernetes 持久化存储与数据卷详解
    本文深入探讨 Kubernetes 中持久化存储的使用场景、PV/PVC/StorageClass 的基本操作及其实现原理,旨在帮助读者理解如何高效管理容器化应用的数据持久化需求。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
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社区 版权所有