Spring的AOP实现原理

article/2025/9/18 10:32:03

本学习笔记将尽可能的将AOP的知识讲解的通俗易懂,先从一个典型的问题出发,引入AOP这个概念,介绍AOP的基本概念,再到Spring中的AOP的实现方案,最后进行一个简单的总结归纳。本学习笔记中不考虑cglib、也不会太关注Spring AOP如何使用,而是尽可能的简单的说清楚AOP的工作原理。

笔记中贴出的源代码均是Spring 5.1.7-RELEASE 版本

问题提出

如下代码块,现在需要统计这个方法执行的耗时情况

public void runTask() {doSomething();
}

一次性的解决肯定非常简单,直接添加一个时间记录即可,如下代码块

public void runTask() {long start = System.currentTimeMillis();doSomething();System.out.println(System.currentTimeMillis() - start);
}
  • 改写原方法:就如上述直接添加时间点记录,针对一两个简单的需求这种方案是最快最高效的,但是弊端也是非常明显的。直接把非业务功能和业务功能耦合在一起、需要改动太大的业务功能、不能灵活修改,如果下一次需要把时间记录去掉,换成统计次数调用,那么所有的地方都得改动,成本非常大,稍有不慎就容易出错
  • 适配包装:即把原对象通过组合的方式包装到一个代理对象中,类似于适配器模式,如下图

⚠️ 这不是说真的就按照适配器模式去开发,而是采取类似的套路。新弄一个类然后新弄一个对应的方法,在新创建的方法里面再具体调用目标对象的方法。AOP也就是为了解决这类问题所提出的一种解决方案。

AOP 的基本概念

AOP(Aspect Oriented Programming)是基于切面编程的,可无侵入的在原本功能的切面层添加自定义代码,一般用于日志收集、权限认证等场景。

在了解AOP包含的组件之前,如果是你去设计实现一套解决方案会如何设计呢?

思考几分钟得处一些必备点~

需要知道在什么地方进行切面操作
需要知道切面操作的具体内容
如果有多个切面操作,应该得有一个先后执行的顺序

事实上AOP也确实是按照这个类似的思路去实现的,先来了解下AOP包含的几个概念

  • Jointpoint(连接点):具体的切面点点抽象概念,可以是在字段、方法上,Spring中具体表现形式是PointCut(切入点),仅作用在方法上。
  • Advice(通知): 在连接点进行的具体操作,如何进行增强处理的,分为前置、后置、异常、最终、环绕五种情况。
  • 目标对象:被AOP框架进行增强处理的对象,也被称为被增强的对象。
  • AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理。
  • Weaving(织入):将增强处理添加到目标对象中,创建一个被增强的对象的过程

总结为一句话就是:在目标对象(target object)的某些方法(jointpoint)添加不同种类的操作(通知、增强操处理),最后通过某些方法(weaving、织入操作)实现一个新的代理目标对象。

动态代理

在继续学习之前有必要介绍一下动态代理。动态代理(Dynamic Proxy)是采用Java的反射技术,在运行时按照某一接口要求创建一个包装了目标对象的新的代理对象,并通过代理对象实现对目标对象的控制操作。

使用动态代理需InvocationHandler + Proxy,可看如下代码块

public class HelloInvocationHandle implements InvocationHandler {private Object object;public HelloInvocationHandle(Object o) {this.object = o;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("method: " + method.getName() + " is invoked");System.out.println("proxy: " + proxy.getClass().getName());Object result = method.invoke(object, args);// 反射方法调用return result;}
}

// HelloWorld 是一个接口,此处没有贴出来
Class<?> proxyClass = Proxy.getProxyClass(HelloWorld.class.getClassLoader(), HelloWorld.class);
Constructor cc = proxyClass.getConstructor(InvocationHandler.class);
InvocationHandler ihs = new HelloInvocationHandle(new HelloWorldImpl());
HelloWorld helloWorld = (HelloWorld) cc.newInstance(ihs);

套路就是先获取Proxy生成的class,然后获取去其中使用了InvocationHandler作为参数的构造器,使用反射newInstance 实现代理对象helloWorld的生成,当然Proxy也提供了更加方便的方法给我们使用

final InvocationHandler in = new HelloInvocationHandle(new HelloWorldImpl());
HelloWorld helloWorld = (HelloWorld) Proxy.newProxyInstance(HelloWorld.class.getClassLoader(),    // 被代理对象的类加载器HelloWorld.class.getInterfaces(),    // 被代理对象的接口(数组,可保护多个)in);   // InvocationHandler实例对象

此外可以使用ProxyGenerator.generateProxyClass方法去获取到动态生成的代理类实际内容,具体如下

继承了Proxy类,而且其构造函数传入的确实是一个InvocationHandler实例

这个方法最后调用相当于InvocationHandler.invoke,经过代理类的保证调用链路就到了HelloInvocationHandle类的invoke方法中,再利用反射调用被代理对象的方法。

接下来就来学习和了解下Spring AOP中最关键的两个类ProxyFactory、ProxyFactoryBean学习AOP的实现原理

ProxyFactory

ProxyFactory或许会比较陌生,可是无论是使用注解的方式还是XML的方式百转千回后还是会去创建ProxyFactory对象,所以忽略Spring前面一系列的操作,利用ProxyFactory做为入口,直面感受和学习AOP代理对象是如何生成的,demo如下

// 本代码来自官方单元测试NameMatchMethodPointcutTests类中的代码
// 在引入spring aop的模块环境下可直接运行
public void setup() {ProxyFactory pf = new ProxyFactory(new SerializablePerson());  // 1nop = new SerializableNopInterceptor();   // 2pc = new NameMatchMethodPointcut();  // 3pf.addAdvisor(new DefaultPointcutAdvisor(pc, nop));  // 4proxied = (Person) pf.getProxy(); // 5// proxied就是生成的AOP代理对象
}

上面的5个步骤每一个都很关键,现在就逐一进行解释

1、ProxyFactory实例化后传入的被代理对象,会被存储到TargetSource对象中,可通过getTarget方法获取到具体的被代理对象,为什么会存储到TargetSource对象中后面会说明,再一个就是获取代理对象可能存在的接口情况也被存储到interfaces列表中。

2、实例化一个SerializableNopInterceptor对象,这是一个实现了MethodInterceptor接口的类,里面的invoke方法是提供给外界触发该增强操作的入口,类图如下:

还记得上面介绍AOP的基本概念时说的Advice通知么?其实这就是一个增强器,包含了我们需要增强的功能,日志的收集、权限认证的具体代码就是写在这些增强器中的。通过调用invoke实现相关的非业务功能。后面会具体说到是谁触发了invoke方法调用。

3、实例化了一个NameMatchMethodPointcut对象,一个非常简单的基于名字匹配的切入点,通俗的说就是通过名字判断是否需要添加通知

其实现了Pointcut接口,并且Pointcut接口包含了ClassFilter getClassFilter();MethodMatcher getMethodMatcher();通过这个名字也能看的出来一个是类过滤器,一个是方法过滤器,两者共同作用就可以判断添加增强器的位置。

曾经使用过Spring AOP的小伙伴们是否记得自己的代码里写过如下类似的注解代码

@Pointcut("@annotation(XXXXAnnotation)")   // 匹配的是 方法添加XXXXAnnotation注解
@Pointcut("execution(public void com.XXXXX.controller.*.*(..))") 
// 匹配的是 public类型 返回void并且是com.XXXXX.controller文件夹下面的所有类方法

从类名称NameMatchMethodPointcut判断是通过方法名称匹配的,可是Pointcut接口却告诉我们是有类匹配和方法匹配两种,那意味着NameMatchMethodPointcut肯定有默认了类过滤的操作,看下StaticMethodMatcherPointcut类,代码如下

public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {private ClassFilter classFilter = ClassFilter.TRUE;// 直接就定义好了类过滤对象ClassFilter.TRUE,也就是下面的TrueClassFilter对象public void setClassFilter(ClassFilter classFilter) {this.classFilter = classFilter;}final class TrueClassFilter implements ClassFilter, Serializable {// 这还是经典的单例写法public static final TrueClassFilter INSTANCE = new TrueClassFilter();private TrueClassFilter() {}@Overridepublic boolean matches(Class<?> clazz) {// 重点在这,默认全部返还truereturn true;}private Object readResolve() {return INSTANCE;}
}

现在知道了NameMatchMethodPointcut是调用了TrueClassFilter单例,所以每一次通过类过滤时,都会返回true,也就是都命中,从而实现了忽略类匹配的操作机制。

到现在2实现了一个通知(增强器)、3实现了一个切入点,那么现在应该需要把2和3组合起来实现切点增强,继续看4

4、pf.addAdvisor(new DefaultPointcutAdvisor(pc, nop));,实例化了DefaultPointcutAdvisor对象,参数传入了实例化好的通知和切入点,形成了一个Advisor添加到了ProxyFacotry的advisors列表中

在实际的spring服务中,可能存在多个通知点和切入点,需要通过各种匹配的规则组合成一系列的Advisor对象,然后添加到对应的ProxyFacotry对象中,以便后面的织入

5、实例化代理对象,pf.getProxy()方法写的是createAopProxy().getProxy();。大致的可以看出来是先创建一个AopProxy对象,然后调用其getProxy()方法返回。先来看看如何创建AopProxy的

// ProxyCreatorSupport 类
private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {this.aopProxyFactory = new DefaultAopProxyFactory();  // 1
}// ProxyFactory 类
public AopProxyFactory getAopProxyFactory() {return this.aopProxyFactory;
}
protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);  // 2
}

原来aopProxyFactory默认就是DefaultAopProxyFactory对象,通过其createAopProxy方法返回一个AopProxy对象,并且这传递的参数是this,有必要贴一下ProxyFactory的UML图

如圈住的地方是一个AdvisedSupport类,也包含了当前代理类的一些信息。来到DefaultAopProxyFactory类

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"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);}
}

参数传递的是AdvisedSupport对象,而ProxyFactory又是继承AdvisedSupport的,所以上面的this参数是正常的。通过对optimize、proxyTargetClas、是否存在对象接口三个条件判断选择是生成JdkDynamicAopProxy还是ObjenesisCglibAopProxy。这里也就是AOP判断使用动态代理还是CGLIB的地方

在xml配置中添加了proxy-target-class属性也就是上面说的config.isProxyTargetClass()判断操作,当设置为true时,就会使用CGLIB

继续深入,进入到JdkDynamicAopProxy的getProxy()方法中

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());}Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);// 明确各种需要实现功能的接口findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

看到这是不是很熟悉,就是我们上面所说的动态代理Proxy.newProxyInstance方法完成代理类的实例化
到这里整个的代理对象就生成了,其实梳理一遍整个流程还是比较清晰的

代理对象调用

动态代理对象生成后调用的入口都是InvocationHandler对象的invoke方法,而且生成代理类的InvocationHandler对象参数传入就是JdkDynamicAopProxy本身

原来JdkDynamicAopProxy也实现了InvocationHandler接口,那么其invoke方法应该包含了具体的调用逻辑

// 精简了很多代码,但是并不影响主流程的学习
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MethodInvocation invocation;Object oldProxy = null;boolean setProxyContext = false;TargetSource targetSource = this.advised.targetSource;Object target = null;try {target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 1 获取增强器执行链if (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);// 2 无增强器调用链,直接通过反射调用target 被代理对象的对应method方法retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {// 3 生成了新的MethodInvocation对象,开始执行增强器调用执行链invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal = invocation.proceed();}return retVal;}
}

1、获取增强器执行链,具体实现在DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice方法中

对切点的过滤匹配,也就是上面说的类过滤和方法过滤,调用类过滤matches方法+方法过滤matches方法,返回true添加到返回的容器中。如果是Interceptor对象则直接添加至返回的容器中。最后生成可被调用的增强器执行链

2、反射method.invoke 调用操作

3、包装成了ReflectiveMethodInvocation对象,然后调用其proceed方法

public Object proceed() throws Throwable {if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {// 运行到最后了执行被代理对象的方法return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);// 从增强器执行链获取一个增强器,索引值currentInterceptorIndex+1if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// 动态参数匹配,匹配后后方可执行InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {return proceed();}}else {// 一般增强器调用invokereturn ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}
}

上面说的 ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);,调用的是methodinterceptor的invoke方法,这地方也就是ProxyFactory开头提的增强器调用invoke操作的调用触发点

来看看@Before注解对应的增强器是如何操作的

@Override
public Object invoke(MethodInvocation mi) throws Throwable {this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());return mi.proceed();
}

和我们设想的一样先执行了增强器的方法,然后循环调用MethodInvocation的proceed的方法,那同理肯定可以猜到@After操作肯定是先执行proceed方法,然后调用相关的增强方法。

到这里整个的AOP过程就算完成了,但是上面还留有一个疑问TargetSource是干什么用的?

上面已经提到targetsource只是包装了一下具体的被代理类,被包装成SingletonTargetSource类,每次获取实际的被代理对象都是通过targetsource.getTarget方法获取的。那我们就可以自定义targetsource改写其中的getTarget()方法,从而实现动态控制被代理对象实际对象了。其实热部署也是采用类似的原理实现的,关于热部署的更多代码可以看看官方提供的HotSwappableTargetSourceTests 单元测试代码。

ProxyFactoryBean

了解完ProxyFactory的整个过程,就很容易理解ProxyFactoryBean了,需知道ProxyFactoryBean = Proxy + FactoryBean,是一种特殊的工厂bean,如下图是其UML类图

通过FactoryBean很自然的想到起代理类是通过getObject方法完成

public Object getObject() throws BeansException {initializeAdvisorChain();// 初始化增强器链,完成advisorif (isSingleton()) {// 依旧需要考虑是否为单例beanreturn getSingletonInstance();}else {if (this.targetName == null) {logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +"Enable prototype proxies by setting the 'targetName' property.");}return newPrototypeInstance();}
}private synchronized Object getSingletonInstance() {if (this.singletonInstance == null) {this.targetSource = freshTargetSource();if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {Class<?> targetClass = getTargetClass();if (targetClass == null) {throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");}setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));}super.setFrozen(this.freezeProxy);this.singletonInstance = getProxy(createAopProxy());}return this.singletonInstance;
}protected Object getProxy(AopProxy aopProxy) {return aopProxy.getProxy(this.proxyClassLoader);
}

需要关注的是this.singletonInstance = getProxy(createAopProxy());aopProxy.getProxy(this.proxyClassLoader);方法,对比发现其和FactoryBean所生成代理对象的方式是一模一样的,先生成AopProxy,再调用AopProxy.getProxy方法。只是其中寻找增强器和切点的逻辑存在差异。



作者:jwfy
链接:https://www.jianshu.com/p/22cf46235d75
来源:简书


http://chatgpt.dhexx.cn/article/KKaLPeT3.shtml

相关文章

spring的AOP和IOC的原理

目录 一、spring的ioc与aop原理 二、代理模式&#xff1a; 三、静态代理 四、动态代理 五、实际的操作 六、动态代理的实现&#xff1a; 七、什么是AOP 八、主流的AOP框架&#xff1a; 九、术语&#xff1a; 十、通知的五种类型&#xff1a; 十一、AOP的优点&#x…

spring aop原理

&#x1f345; Java学习路线&#xff1a;搬砖工逆袭Java架构师 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、CSDN哪吒公众号作者✌ 、Java架构师奋斗者&#x1f4aa; &#x1f345; 扫描主页左侧二维码&#xff0c;加入群聊&#xff0c;一起学习、一起进步…

AOP原理

AOP原理 什么是AopAOP的作用AOP的基本概念AOP使用场景AOP原理如何动态修改功能AOP的编程思想AOP面向切面编程操作AOP通知执行的顺序代码执行流程准备工作源码揭开面纱 什么是Aop AOP&#xff08;Aspect Orient Programming&#xff09;也就是面向切面编程&#xff0c;作为面向对…

无法显示页面,因为发生内部服务器错误。

用iis添加网站后&#xff0c;访问域名&#xff0c;显示“无法显示页面&#xff0c;因为发生内部服务器错误。” 之前我将这些文件放在一个Demo文件夹中&#xff0c;把Demo放在test站点下&#xff0c;通过域名访问Demo报错。 我将Demo下的文件直接放在test下&#xff0c;访问成…

遇到“服务器内部错误http500怎么办?

出现500错误的原因是很多的&#xff0c;一般来说都是程序错误导致的&#xff0c;如果程序出错&#xff0c;那么在浏览器内会返回给用户一个友好的错误提示&#xff0c;统一称之为服务器500错误。 解决的方法就是您必须在http中能够正确的获得错误信息&#xff0c;方法为&#x…

500 - 内部服务器错误--解决方案

一般网上的方法是这样的&#xff1a; 一、打开 Internet 信息服务(IIS)管理器。点击出错的站点&#xff0c;并双击右边的ASP图标&#xff0c;如下图所示&#xff1a; 二、展开右侧配置中的“调试属性”&#xff0c;把“将错误发送到浏览器”的值设为 "true"&#xf…

HTTP状态 500 - 内部服务器错误 类型 异常报告,初学servlet遇到的问题

写给自己看&#xff0c;初学记录一下&#xff0c;maven项目中tomcat,Servlet遇到的问题 HTTP状态 500 - 内部服务器错误 类型 异常报告&#xff0c;初学servlet遇到的问题 类似这种报错&#xff0c;在hello world级别的servlet中碰到。 报错分析 大概是说自定义java类在实例…

IIS 配置网站出现500内部服务器错误,显示具体错误信息

1、打开IIS 找到如下图的部分&#xff0c;双击点开 2、点开之后找到如下图部分&#xff0c;点击 3、选择如下图部分&#xff0c;然后点击确定。 4、这个时候页面会出现详细的错误&#xff0c;如果没有出现详细错误&#xff0c;配置如下图部分&#xff0c;不勾选。 5、这时会出现…

Nextcloud 内部服务器错误解决

在部署nextcloud过程中最后登录页面时出现内部服务器错误&#xff0c;心态当时就炸了。 在网上找了各种方案&#xff0c;但是大部分博主都告诉我是/var/lib/php/session/属组的权限问题&#xff0c;或者web目录的权限不对。但是对我这个问题没有用。 我把他们的方案贴出来对你…

IDEA中HTTP500 - 内部服务器错误类型 在 [] 行处理 [/.jsp] 时发生异常情况;java.lang.NoSuchMethodError: com.Bean.Person.setId

问题&#xff1a; HTTP状态 500 - 内部服务器错误 类型 异常报告 消息 在 [65] 行处理 [/pages/el 5/elDataDemo2.jsp] 时发生异常 描述 服务器遇到一个意外的情况&#xff0c;阻止它完成请求。 例外情况 org.apache.jasper.JasperException: 在 [65] 行处理 [/pages/el 5/elDa…

HTTP状态 500 - 内部服务器错误:No converter found for return value of type: class xxx(简单分析及解决)

问题描述 以下内容基于ssm框架&#xff0c;当我们向tomcat服务器发起请求时&#xff0c;出现如下的错误状态提示–500。 Tomcat日志信息&#xff1a; 原因分析&#xff1a; 未找到类型返回值的转换器&#xff1a;类 com.ssm.utils.Msg&#xff0c;使用jackson绑定数据时出现…

阿里云服务器出错500 - 内部服务器错误

阿里云服务器部署并发布成功后&#xff0c;访问该网页时出错&#xff0c;报 500 - 内部服务器错误。 原因&#xff1a;1.http 500内部服务器错误说明IIS服务器无法解析ASP代码&#xff0c;如果你联网还发现找不到服务器就是500错误了. 2.在安装Framework v4.0之后&#xff0c…

HTTP 500 - Internal Server Error 服务器内部错误

php出现如下错误 原因是出现了中文字符 修改后&#xff0c;页面成功访问

500错误及服务器内部错误

500错误及服务器请求错误 一、简言&#xff1a;500错误代表着你请求的后端或者说服务器端出现了错误。 可能的原因非常多&#xff0c;因为很多程序内部都会把代码运行出现的错误捕捉然后直接返回一个“服务器内部错误500”&#xff0c;这也就导致了我们在前端发送请求时见到的…

远程服务器返回错误: (500) 内部服务器错误解决办法

在.net中发送 xml post请求和接受xml 的post请求时&#xff0c;经常会遇到“远程服务器返回错误: (500) 内部服务器错误”。 这里有2种解决办法&#xff1a; 第一种方法&#xff1a;修改请求端Content-Type 为“text/xml”(httpclient.Headers.Add("Content-Type",…

服务器错误500-内部服务器错误。您查找的资源存在问题,因而无法显示。

当我们用iis环境搭建的网站出现了服务器错误500错误时我们该怎么处理呢&#xff0c;小编的网站织梦网站就出现了服务器错误 500 - 内部服务器错误。 您查找的资源存在问题&#xff0c;因而无法显示这个问题&#xff0c;如下图所示。织梦网站后台登陆前台预览都是正常的&#x…

什么是HTTP 500内部服务器错误,要怎么修复

HTTP 500是一种原始的错误代码&#xff0c;它指示网站服务器在处理请求时发生了内部错误&#xff0c;不过具体错误原因是不确定的。一般情况下&#xff0c;这种错误通常是由服务器程序上的bug或者配置问题造成的。当服务器收到请求时&#xff0c;尝试执行它时&#xff0c;但是发…

HTTP状态500-内部服务器错误

SSM整合时出现的问题 起初关注点一直在Mapper.xml上&#xff0c;CSDN查询解决方法大致在这样几种。 1.SQL语句问题 2.resultType返回类型问题&#xff08;List集合中的参数类型&#xff09; 3.database.properties数据库配置文件问题&#xff08;Mysql8以上加时区&#xff0c;文…

Java问题解决:HTTP状态 500 - 内部服务器错误 Servlet[SpringMVC]的Servlet.init()引发异常

问题&#xff1a; 在学习11_尚硅谷_SpringMVC_HelloWorld 课程中部署SpringMVC项目时遇到如下问题&#xff1a; HTTP状态 500 - 内部服务器错误 类型 异常报告消息 Servlet[SpringMVC]的Servlet.init&#xff08;&#xff09;引发异常描述 服务器遇到一个意外的情况&#xff0…

“500 - 内部服务器错误。”解决办法

建站时遇到如下页面错误&#xff1a; 解决办法&#xff1a; 打开计算机管理IIS后&#xff0c;展开网站&#xff0c;点击出现错误的具体网站->"高级设置"->"物理路径凭据", 在弹出的对话框中查看路径凭据是不是选择在"特定用户",如果是请改…