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







推荐阅读
  • 本文将带你快速了解 SpringMVC 框架的基本使用方法,通过实现一个简单的 Controller 并在浏览器中访问,展示 SpringMVC 的强大与简便。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 属性类 `Properties` 是 `Hashtable` 类的子类,用于存储键值对形式的数据。该类在 Java 中广泛应用于配置文件的读取与写入,支持字符串类型的键和值。通过 `Properties` 类,开发者可以方便地进行配置信息的管理,确保应用程序的灵活性和可维护性。此外,`Properties` 类还提供了加载和保存属性文件的方法,使其在实际开发中具有较高的实用价值。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 在本文中,我们将为 HelloWorld 项目添加视图组件,以确保控制器返回的视图路径能够正确映射到指定页面。这一步骤将为后续的测试和开发奠定基础。首先,我们将介绍如何配置视图解析器,以便 SpringMVC 能够识别并渲染相应的视图文件。 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • Spring框架的核心组件与架构解析 ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
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社区 版权所有