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







推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
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社区 版权所有