一、创建代理准备工作
回顾之前的wrapIfNecessary 方法,再获取到匹配的增强器后,Spring就会为拦截的bean创建代理对象:
//获取匹配的增强器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//DO_NOT_PROXY = null
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
创建代理createProxy:
protected Object createProxy(
Class beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
//在bean对应的BeanDefinition中添加原Class对象属性(保存bean原本的Class)
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
//获取当前类中配置的属性
proxyFactory.copyFrom(this);
//检查proxyTargetClass属性
if (!proxyFactory.isProxyTargetClass()) {
//检查beanDefinitioin中是否包含preserveTargetClass属性,且属性为true
//设置是否使用CGLib进行代理
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
//筛选代理接口并添加到proxyFactory
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//获取增强器(包括前面筛选出来的增强器,以及通过setInterceptorNames中添加的通用增强器,默认为空)
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
//将所有增强器添加到proxyFactory
proxyFactory.addAdvisor(advisor);
}
//设置需要代理的bean对象信息
proxyFactory.setTargetSource(targetSource);
//模版方法,由子类定制化代理
customizeProxyFactory(proxyFactory);
//用来控制代理工程被配置后,是否还允许修改代理的配置,默认为false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
上面代理流程为:
在BeanDefinition中保存bean原Class对象,因为创建代理后,bean的class会被修改(Spring4中新加入的带你,Spring3中不包含)
创建ProxyFactory
设置属性
过滤目标bean的接口,并添加到ProxyFactory
获取增强器实例,添加到ProxyFactory中
创建代理
过滤接口
过滤接口 中主要功能是,帮助判断是否使用JDK的动态代理来创建代理。因为JDK动态代理的条件是bean实现了接口 ,所以Spring会将目标bean实现的接口过滤后添加到ProxyFactory中,方便判断是否使用JDK动态代理,下面是evaluateProxyInterfaces 实现:
protected void evaluateProxyInterfaces(Class beanClass, ProxyFactory proxyFactory) {
//获取所有实现的接口
Class[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class ifc : targetInterfaces) {
//不是Spring内部回调用的接口 && 不是语言内部接口 && 接口定义了一个以上的方法
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
//如果满足上面三个条件,才会将接口添加到proxyFactory
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
for (Class ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
//条件不满足,缺少合适的接口,无法使用JDK动态代理,使用CGLib
else {
proxyFactory.setProxyTargetClass(true);
}
}
下面是过滤条件:
1.Spring内部接口
会排除掉InitializingBean、DisposableBean、Aware接口
protected boolean isConfigurationCallbackInterface(Class ifc) {
return (InitializingBean.class == ifc || DisposableBean.class == ifc ||
ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}
2、是否是语言内部接口 isInternalLanguageInterface
protected boolean isInternalLanguageInterface(Class ifc) {
return (ifc.getName().equals("groovy.lang.GroovyObject") ||
ifc.getName().endsWith(".cglib.proxy.Factory"));
}
其实就是比较类名
3、接口中是否定义了方法
二、创建代理
ProxyFactory.getProxy:
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
createAopProxy:
protected final synchronized AopProxy createAopProxy() {
//active会在在第一次创建代理后,设为true
if (!this.active) {
//设置active为true,并通知监听器(如果没有配置,为空)
activate();
}
//getAopProxyFactory会返回aopProxyFactory变量,默认实现为DefaultAopProxyFactory
return getAopProxyFactory().createAopProxy(this);
}
代码流程:
如果是第一次创建代理,会通知ProxyFactory中注册的监听器
获取类中定义的aopProxyFactory变量,默认实现为DefaultAopProxyFactory
通过DefaultAopProxyFactory创建代理对象(会传入当前对象,用于获取配置信息)
DefaultAopProxyFactory-->createAopProxy:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
//代理目标bean的Class不能为空
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.");
}
//如果是接口 或者 Class类型为Proxy
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
Spring生成代理对象的方式有两种,JDK动态代理和CGLib,分别生成JdkDynamicAopProxy和ObjenesisCglibAopProxy,从上面代码可以看出Spring的判断条件 :
optimize:用来控制通过CGLib创建的代理是否使用激进的优化策略(该仅对CGLib有效)
proxyTargetClass:当属性为true,使用CGLib,设置方式:。
是否存在代理接口(也就是前面过滤接口一节中,添加进去的接口)
如果目标类是接口的话,还是会使用JDK的方式进行代理
JDK动态代理与CGLib的区别:
JDK只能针对实现了接口的类生成代理
CGLib是针对类实现代理,主要通过生成目标类的子类,覆盖其中的方法来达到代理的目的。因此,目标类或方法不能被定义为final
三、获取代理对象
回顾之前getProxy创建代理的代码:
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
getAopProxy就是上一节中获取到的AopProxy实例:JdkDynamicAopProxy或ObjenesisCglibAopProxy ,后面会调用其getProxy获取代理对象:
public Object getProxy(ClassLoader classLoader) {
//log...
//获取代理接口
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
//检查是否在接口中定义了equals或hashCode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//创建代理对象
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
上面代码流程:
获取代理接口
检查是否在接口中定义了equals或hashCode方法
使用JDK动态代理Proxy.newProxyInstance创建代理对象
四、方法拦截过程
JDK生成代理对象需要调用return Proxy.newProxyInstance,并传入类加载器、代理接口、以及一个InvocationHandler实例。其中InvocationHandler的invoke方法定义了代理的流程。
上面代码中,传入的InvocationHandler是this,即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;
Class targetClass = null;
Object target = null;
try {
//如果接口中定义了equals或hashCode方法,则进行专门处理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
//opaque属性,表示是否禁止将代理对象转换为Advised对象,默认是false
//如果调用的方法来自Advised接口
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
//通过反射Method.invoke,调用advised(传入的ProxyFactory实例,该类实现了Advised接口)对应的方法
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
//为了解决目标对象内部的自我调用无法实施切面中的增强,需要暴露代理对象
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取目标对象信息
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取当前方法的拦截器链
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 如果没有任何拦截器,则调用直接对原目标对象调用方法
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 将拦截器链封装到ReflectiveMethodInvocation,方便使用其proceed进行链式调用拦截器
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截器链
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// 特殊情况:为了防止方法返回"return this",返回原目标对象,会将返回值替换为代理对象
retVal = proxy;
}
//如果返回类型是基本类型兵器,但是返回结果为null,抛出异常
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
代码流程:
特殊方法的处理,包括equals 、hashCode 方法以及定义在Advised接口 中的方法
获取方法匹配的增强器,并生成拦截器链并调用
对方法返回结果进行处理:如果返回“this”,即原目标对象,则会替换为返回代理对象;如果返回结果为null,但返回类型为基本数据类型(int、char等)则抛出异常
1、获取匹配的拦截器:
public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
//缓存的cacheKey,先尝试从缓存获取,不存在再从ProxyFactory中解析
MethodCacheKey cacheKey = new MethodCacheKey(method);
List cached = this.methodCache.get(cacheKey);
if (cached == null) {
//通过实例方法和ProxyFactory中保存的信息,解析出匹配的增强
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
方法匹配增强器的功能交由advisorChainFactory(DefaultAdvisorChainFactory)完成:
public List getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
List interceptorList = new ArrayList(config.getAdvisors().length);
Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
//方法是否匹配引介增强
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//遍历增强器
for (Advisor advisor : config.getAdvisors()) {
//PointcutAdvisor类型的增强器
//通过@Aspect加入的增强器类型为InstantiationModelAwarePointcutAdvisorImpl,实现了PointcutAdvisor
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
//对于引介增强的处理
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
上面代码首先会遍历拦截增强器,然后根据目标类和目标方法(引介增强外)匹配增强器,如果匹配的话,返回增强器包装的拦截器(Advice)
对于@Aspect注解配置类中,@Before、@After、@AfterThrowing配置的增强会被包装为AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice、AspectJAfterThrowingAdvice等,在这一步骤中,这些Advice会分别被封装为MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor拦截器。
2、创建拦截器链并调用
Spring会在获取到方法匹配的拦截器后,将代理对象、目标对象、调用方法、参数、拦截器等信息封装到ReflectiveMethodInvocation 中:
protected ReflectiveMethodInvocation(
Object proxy, Object target, Method method, Object[] arguments,
Class targetClass, List interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
然后调用ReflectiveMethodInvocation的proceed方法:
public Object proceed() throws Throwable {
// 执行完所有增强后,执行切点方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
//动态匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//不匹配则不执行拦截器
return proceed();
}
}
else {
// 普通拦截器。比如:MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
通过currentInterceptorIndex记录当前拦截器的索引,每调用一个拦截器就+1,再次调用proceed方法时就会获取下一个拦截器调用。
3、拦截器调用
在获取增强器的过程中,例如@Before注解标注的方法会被封装为AspectJMethodBeforeAdvice ,然后会在 1.获取匹配的拦截器 过程中,会被包装为MethodInterceptor(MethodBeforeAdviceInterceptor),下面是Advice转换为MethodInterceptor的过程:
DefaultAdvisorAdapterRegistry.getInterceptors:
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List interceptors = new ArrayList(3);
//获取Advice
Advice advice = advisor.getAdvice();
//如果Advice实例同时已经实现MethodInterceptor接口,则直接使用
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//需要使用适配器来转换Advice接口
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
代码流程:
如果Advice增强已经实现了MethodInterceptor,则不需要转换,可以直接使用。例如:@After注解标注的增强方法会被表示为AspectJAfterAdvice,该类同时实现了Advice和MethodInterceptor接口,也就是已经在类中规定好了拦截的逻辑。
Advice实现了没有同时实现MethodInterceptor,所以需要使用内置的适配器将Advice增强转换为MethodInterceptor拦截器。
a、内置的Advice适配器
在前面的代码中,将Advice转换为MethodInterceptor的工作是交给this.adapters来完成的,该变量定义如下;
private final List adapters = new ArrayList(3);
并且在DefaultAdvisorAdapterRegistry的构造函数中,对该变量进行了初始化填充:
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
可以看到,Spring实现会加入3个默认的是Advice适配器:MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter。
这三个适配器通过support方法,验证是否是否支持传入的Advice对象,如果支持,会将Advice实例封装为对应的MethodInterceptor实例:
MethodBeforeAdviceAdapter:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
AfterReturningAdviceAdapter:
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
ThrowsAdviceAdapter:
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
下面是对三种适配器的总结:
适配器
支持的Advice接口
Advice实现类
对应注解
MethodBeforeAdviceAdapter
MethodBeforeAdvice
AspectJMethodBeforeAdvice
@Before
AfterReturningAdviceAdapter
AfterReturningAdvice
AspectJAfterReturningAdvice
@AfterReturning
ThrowsAdviceAdapter
ThrowsAdvice
无
无
下面再来总结一下通过注解配置的增强对应的拦截器:
注解
Advice
MethodInterceptor
@Before
AspectJMethodBeforeAdvice
MethodBeforeAdviceInterceptor
@After
AspectJAfterAdvice
AspectJAfterAdvice
@AfterReturning
AspectJAfterReturningAdvice
AfterReturningAdviceInterceptor
@AfterThrowing
AspectJAfterThrowingAdvice
AspectJAfterThrowingAdvice
@Around
AspectJAroundAdvice
AspectJAroundAdvice
b、拦截器调用
拦截器的类型有多中,下面我们分析由@Before注解添加的拦截器MethodBeforeAdviceInterceptor。
MethodBeforeAdviceInterceptor定义:
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
private MethodBeforeAdvice advice;
/**
* 将传入的MethodBeforeAdvice封装为MethodBeforeAdviceInterceptor
* @param advice the MethodBeforeAdvice to wrap
*/
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
从invoke方法中可以看到,拦截器会在调用真正方法前,调用MethodBeforeAdvice的before方法 ,在这一步,也就完成了方法的前置增强。
下面看一下AspectJMethodBeforeAdvice的before实现:
public void before(Method method, Object[] args, Object target) throws Throwable {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
//将参数绑定后,传入invokeAdviceMethodWithGivenArgs方法执行
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
invokeAdviceMethodWithGivenArgs:
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
//如果advice增强方法参数为空
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// 通过反射,调用增强方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
你可能感兴趣的:(spring,源码)
Springboot 自动化装配的原理
Code额
SpringBoot spring boot java
Springboot自动化装配的原理SpringBoot主要作用为:起步依赖、自动装配。而为了实现这种功能,SpringBoot底层主要使用了@SpringBootApplication注解。首先,@SpringBootApplication是一个复合注解,它结合了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解的功能。这个注解通
springboot-利用反射调用service层方法从Excel读取数据写入数据库报错:java.lang.NullPointerException,小坑一个
正在努力中。。。
springboot java 数据库 spring boot
最近在做一个小项目,需要从读取Excel数据写入到数据库。利用的是easyExcel的read方法,通过实现了ReadListener接口的监听器去调用service层方法实现数据持久化操作。但业务表那么多,本身在读取过程中不需要做任何操作,只要数据从Excel读出来直接写到mysql就可以,因此不想给每个表都写一个监听器,通过泛型加反射的方式实现类的匹配和方法调用。我的方案是:1.前台选择Exc
java接口返回值实现数据脱敏
不平衡的叉叉树
java java 开发语言
基于jackson,通过自定义注解的方式实现数据脱敏,在需要脱敏的字段上使用该注解即可。由于项目是springboot服务,而jackson相关依赖包已由spring-web、spring-boot-starter-web集成,所以无需单独添加jackson相关依赖包。自定义脱敏注解@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInsi
微信小程序游戏源码:全开源可二开
javascript
微信小程序游戏源码技术选型在微信小程序游戏源码的技术选型过程中,开发者需要综合考虑多个因素,包括游戏性能、开发效率、跨平台兼容性、团队技术栈以及社区支持等。以下是一个详细的微信小程序游戏源码技术选型清单,旨在帮助开发者做出明智的决策。源码及演示:casgams.top/gm1、原生框架优势:性能优越:原生框架直接使用微信官方提供的开发工具和框架,如WXML(布局文件)、WXSS(样式文件)、Jav
MES生产管理系统源码,万界星空科技开源MES
开源运维开源软件工业互联网源码
MES生产管理系统源码,实时监控生产过程、管理生产数据、优化生产计划MES生产制造执行系统源码,可以试用,支持二次开发,商业授权后可商用;万界星空科技MES,公司提供项目合作、分析、指导及技术支持。MES是“ManufacturingExecutionSystem”的英文缩写,中文翻译为“制造执行系统”“生产实施系统”等。是一种用于实时监控、追踪和控制生产过程的计算机化系统。MES系统可以对生产线
Spring Boot 整合WebSocket
weixin_44021967
java websocket
文章目录1.WebSocket介绍2.SpringBoot结合WebSocket11-03在线私聊1.WebSocket介绍SpringBoot结合WebSocket实现在线聊天:https://mp.weixin.qq.com/s/Rrj7oco3K8kJBsTxEEdz9gWebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服
【开源dcluster】一站式数据服务分析平台IDEA本地开发指南
dcluster开源
大数据
源码Gitee地址:https://gitee.com/zhenglv123456/dcluster在线文档:http://47.121.127.33:8090/在线体验:http://36.155.14.171:12345/dolphinscheduler/ui/login账号密码:test/test123源码启动1.前端启动进入dolphinscheduler-ui目录,执行以下命令npmin
批量检查微信小程序是否被封禁的Python脚本
python
概述:此Python脚本用于批量请求微信小程序的封禁状态。脚本通过访问API接口https://api.52an.fun/xcx/checkxcx.php?appid=wx81894c6dbb81c2e2来获取每个小程序的封禁状态。接口返回的JSON格式数据包含了封禁状态信息,其中"code":0表示小程序已被封禁,"code":1表示小程序正常。脚本源码:importrequestsimport
【微服务-SpringCloud】详细介绍,搭建一套微服务项目
Tank_Chen
Spring java spring boot spring cloud
目录微服务介绍微服务的特点微服务的优势微服务的挑战SpringCloud介绍SpringCloud核心概念为什么使用SpringCloudSpringCloud使用项目案例技术栈环境准备创建项目用户服务(UserService)pom.xmlapplication.ymlUserController.javaUserService启动类订单服务(OrderService)pom.xmlapplic
Spring Boot整合WebSocket实现实时通信
Mac-AcAclA
spring boot websocket 后端
SpringBoot整合WebSocket实现实时通信引言在现代Web应用中,实时通信功能变得越来越重要。WebSocket提供了一种在客户端和服务器之间进行全双工通信的方式,使得服务器能够主动向客户端推送消息。SpringBoot作为一个轻量级的Java应用框架,提供了对WebSocket的原生支持,使得开发者可以轻松地在SpringBoot应用中集成WebSocket。本文将详细介绍如何在Sp
微服务实战:Spring Boot与Spring Cloud指南
萧书泓
微服务实战:SpringBoot与SpringCloud指南Microservices-with-Spring-Boot-and-Spring-Cloud-Third-EditionMicroserviceswithSpringBootandSpringCloud,ThirdEdition项目地址:https://gitcode.com/gh_mirrors/mi/Microservices-wi
【星云 Orbit-F4 开发板】01. STM32F407 HAL库开发环境安装与工程模板创建
智木芯语
【星云 Orbit-F4 开发板】 【星火 Orbit-F1开发板】 单片机 stm32 物联网 mcu 嵌入式硬件
这几天规划下,计划为我的学生出一个星系列的四个开发板,所有原理图,印板图和例程源码全部开源,博客上文章配合开发板硬件电路设计,不定时发布。不忘初心,用我的执着致力点燃学生对技术的热情,培养“懂理论能实战”的嵌入式人才。愿你们所行风雨无阻,前程似锦。核心板型号处理器参数学习定位典型应用场景极光Orbit-C8STC8H8K64U(51架构/24MHz)单片机原理与应用GPIO控制/UART通信星火O
保姆级!springboot访问Ollama API并调用DeepSeek模型 Api
不会吉他的肌肉男不是好的挨踢男
spring boot DeepSeek Ollama ai 大模型
要在springboot中访问OllamaAPI并调用DeepSeek模型,你需要遵循以下步骤。首先,确保你有一个有效的Ollama服务器实例运行中,并且DeepSeek模型已经被加载。可以参考我的这篇博客保姆级!使用Ollama本地部署DeepSeek-R1大模型并java通过api调用使用SpringBoot+SpringAI在使用Ollama把deepseek-r1跑起来之后,我们就可以开始
Spring Cloud Data Flow 简介
m0_74825614
面试 学习路线 阿里巴巴 spring cloud spring 后端
SpringCloudDataFlow介绍1.Dataflow是一个用于开发和执行大范围数据处理其模式包括ETL,批量运算和持续运算的统一编程模型和托管服务。2.对于在现代运行环境中可组合的微服务程序来说,springclouddataflow是一个原生云可编配的服务。使用springclouddataflow,开发者可以为像数据抽取,实时分析,和数据导入/导出这种常见用例创建和编配数据通道(da
Spring Boot 排除自动配置
m0_74824954
面试 学习路线 阿里巴巴 spring boot java mybatis
SpringBoot排除自动配置SpringBoot自动配置非常强大,有时需要排除/禁用SpringBoot某些类的自动化配置。***************************APPLICATIONFAILEDTOSTART***************************Description:FailedtoconfigureaDataSource:'url'attributeisn
SpringBoot 与 Maven 快速上手指南
蓝天资源分享
spring boot maven 后端
SpringBoot与Maven快速上手指南在Java开发领域,SpringBoot和Maven是两个极其重要的工具,它们极大地简化了企业级应用的开发和构建过程。SpringBoot通过自动配置和起步依赖等特性,让开发者能够快速搭建起一个Spring应用;而Maven则是一个强大的项目管理和构建工具,能够帮助我们管理项目的依赖、构建生命周期以及自动化测试等。本文将详细介绍如何快速上手SpringB
【异常】MyBatis-Plus因@TableId错误使用,导致主键未生成,SQL异常提示SQLException: Field ‘id‘ doesn‘t have a default value
本本本添哥
004 - 数据库 sql mybatis java mysql spring boot
一、异常内容org.springframework.dao.DataIntegrityViolationException:###Errorupdatingdatabase.Cause:java.sql.SQLException:Field'crash_id'doesn'thaveadefaultvalue###Theerrormayexistincom/xxx/terminal/mapper/C
响应式前端开发框架Bootstrap实战应用
马屿人
本文还有配套的精品资源,点击获取简介:Bootstrap是广受欢迎的前端框架,用于创建响应式和移动优先的网站。结合JavaEE、Spring、SpringMVC和MyBatis后端技术,开发者可利用Bootstrap快速开发出模块化、用户友好的Web应用。该框架含有预定义的CSS样式、JavaScript插件、HTML模板和更多资源,以支持高效和模块化的Web应用开发流程。本压缩包中可能包含Boo
Spring Boot与MyBatis
geinvse_seg
面试 学习路线 阿里巴巴 spring boot mybatis 后端
SpringBoot与MyBatis的配置一、简介SpringBoot是一个用于创建独立的、基于Spring的生产级应用程序的框架,它简化了Spring应用的初始搭建以及开发过程。MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。将SpringBoot和MyBatis结合使用,可以高效地开发数据驱动的应用程序。二、环境准备(一)创建SpringBoot项目可以使用Sp
Ubuntu,centos下源码安装cmake指定版本
你若盛开,清风自来!
ubuntu centos linux
网址:Indexof/files/v3.23常规安装出错1.先把安装包cmake-3.12.4-Linux-x86_64.tar.gz复制到指定目录2.解压tar-zxvfcmake-3.12.4-Linux-x86_64.tar.gz3.进入解压之后的文件夹cdcmake-3.12.4-Linux-x86_64.tar.gz4.运行下面命令出错bash:./bootstrap:Nosuchfil
SpringBoot备份神通数据库
松岛的枫叶
spring boot 数据库 后端
SpringBoot备份神通数据库····直接上代码···publicStringbackupOsrdb(){//构建命令Listcommand=newArrayList<>();command.add("osrexp");command.add("-u");command.add("SYSDBA"+"/"+"szoscar55");//用户名/密码command.add("-d");comman
【面试实战】Spring基础、IoC、AOP、MVC、Mybatis、Spring Boot
Sivan_Xin
技术实战专栏(已上云) 面试 mvc spring
version:1.0文章目录SpringSpring基础/IoC♂️面试官:举例Spring的模块?♂️面试官:Spring、SpringMVC、SpringBoot关系?♂️面试官:说说对SpringIoC的了解?♂️面试官:什么是SpringBean?♂️面试官:Bean的作用域?♂️面试官:Bean的生命周期?♂️面试官:单例Bean的线程安全问题了解吗?♂️面试官:@Co
说说 Spring MVC 的执行流程?
浮生带你学Java
Java面试题 Spring spring mvc java
高频面试题:说说SpringMVC的执行流程?大家好,我是浮生,一个工作了十四年的java程序员!昨天,一个工作2年的粉丝在面试的时候,面试官要求他说SpringMVC的执行流程。他没回答上来,错过了这个offer。一、问题解析SpringMVC的执行流程,一个面试频率超级高的问题,但是缺难倒了无数的程序员。这个问题的考察范围主要是3~5年,甚至5年以上都会问到。和它同类型的问题还有Bean的加载
SpringCloud/Boot集成LogBack
azoon.top
spring cloud logback spring log4j slf4j
一.简要介绍什么是SLF4J?官网介绍:SimpleLoggingFacadeforJava(SLF4J)充当简单的各种日志记录框架的Facade或抽象(e.g.java.util.logging、logback、log4j)允许最终用户在部署时插入所需的日志记录框架。类似java中的接口,如果只集成SLF4J,日志只能输出在控制台,并没有输出到文件的能力,要实现真正的日志能力,需要引入其实现层:
spring boot、spring cloud、spring cloud alibaba 之间的版本对应关系
薄荷街的兔比先生
spring boot spring cloud java
大家在引用pom的时候还是要以官方推荐版本为主,不然容易出现未知的问题。1.springboot和springcloud的版本关系官方提供的版本对照表https://start.spring.io/actuator/info{"git":{"branch":"93e528fc7e79e41a513e493d057499401a15eb67","commit":{"id":"93e528f","ti
根据Excel生成建表语句sql——源码设计说明
忙碌的菠萝
java 环境搭建 sql java 数据库
根据Excel生成建表语句sql设计的人跟开发的人总不是同一个,这就导致了设计是设计的思路,开发是开发的思路,表也是一样,开发给加了字段不同步给设计人员,设计加了字段开发可能这个环境加了,另一个没加。为了避免比对和扯皮,以设计为准!序号内容连接地址1工具使用说明https://blog.csdn.net/qq_21271511/article/details/1219010642工具下载地址htt
SpringBoot + vue 管理系统
m0_74825565
面试 学习路线 阿里巴巴 spring boot vue.js 后端
SpringBoot+vue管理系统文章目录SpringBoot+vue管理系统1、成品效果展示2、项目准备3、项目开发3.1、部门管理3.1.1、前端核心代码3.1.2、后端代码实现3.2、员工管理3.2.1、前端核心代码3.2.2、后端代码实现3.3、班级管理3.3.1、前端核心代码3.3.2、后端代码实现3.4、学生管理3.4.1、前端核心代码3.4.2、后端代码实现3.5、数据统计3.5.
Spring 核心技术解析【纯干货版】- XII:Spring 数据访问模块 Spring-R2dbc 模块精讲
m0_74825003
面试 学习路线 阿里巴巴 spring java 后端
在现代应用架构中,高并发、低延迟的需求推动了响应式编程的发展,而传统的JDBC由于其同步阻塞机制,在高吞吐场景下可能成为瓶颈。R2DBC(ReactiveRelationalDatabaseConnectivity)作为响应式关系型数据库访问标准,正是为了解决这一问题而诞生的。SpringR2DBC作为Spring生态对R2DBC的封装,提供了非阻塞、异步的数据库访问能力,并与SpringWebF
Spring Boot的项目结构
m0_74823983
面试 学习路线 阿里巴巴 spring boot 后端 java
SpringBoot的项目结构技术背景SpringBoot项目结构遵循Maven或Gradle的标准目录结构,同时融入了SpringBoot的特定约定。良好的项目结构不仅有助于代码组织,还能提高开发效率和项目可维护性。了解SpringBoot的项目结构对于开发高质量的应用至关重要。1.基础项目结构1.1标准目录结构基本的SpringBoot项目结构如下:myproject/├──src/│├──m
Qt5.6在Linux中无法切换中文输入法问题解决
糯米藕片
经验分享 qt linux 开发语言
注意Qt5.6.1要编译1.0.6版本源码chmod777赋权复制两个地方so重启QtCreatorsudocplibfcitxplatforminputcontextplugin.so/home/shen/Qt5.6.1/Tools/QtCreator/lib/Qt/plugins/platforminputcontextssudocplibfcitxplatforminputcontextpl
Spring4.1新特性——综述
jinnianshilongnian
spring 4.1
目录
Spring4.1新特性——综述
Spring4.1新特性——Spring核心部分及其他
Spring4.1新特性——Spring缓存框架增强
Spring4.1新特性——异步调用和事件机制的异常处理
Spring4.1新特性——数据库集成测试脚本初始化
Spring4.1新特性——Spring MVC增强
Spring4.1新特性——页面自动化测试框架Spring MVC T
Schema与数据类型优化
annan211
数据结构 mysql
目前商城的数据库设计真是一塌糊涂,表堆叠让人不忍直视,无脑的架构师,说了也不听。
在数据库设计之初,就应该仔细揣摩可能会有哪些查询,有没有更复杂的查询,而不是仅仅突出
很表面的业务需求,这样做会让你的数据库性能成倍提高,当然,丑陋的架构师是不会这样去考虑问题的。
选择优化的数据类型
1 更小的通常更好
更小的数据类型通常更快,因为他们占用更少的磁盘、内存和cpu缓存,
第一节 HTML概要学习
chenke
html Web css
第一节 HTML概要学习
1. 什么是HTML
HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,它规定了自己的语法规则,用来表示比“文本”更丰富的意义,比如图片,表格,链接等。浏览器(IE,FireFox等)软件知道HTML语言的语法,可以用来查看HTML文档。目前互联网上的绝大部分网页都是使用HTML编写的。
打开记事本 输入一下内
MyEclipse里部分习惯的更改
Array_06
eclipse
继续补充中----------------------
1.更改自己合适快捷键windows-->prefences-->java-->editor-->Content Assist-->
Activation triggers for java的右侧“.”就可以改变常用的快捷键
选中 Text
近一个月的面试总结
cugfy
面试
本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/46753275
前言
打算换个工作,近一个月面试了不少的公司,下面将一些面试经验和思考分享给大家。另外校招也快要开始了,为在校的学生提供一些经验供参考,希望都能找到满意的工作。
HTML5一个小迷宫游戏
357029540
html5
通过《HTML5游戏开发》摘抄了一个小迷宫游戏,感觉还不错,可以画画,写字,把摘抄的代码放上来分享下,喜欢的同学可以拿来玩玩!
<html>
<head>
<title>创建运行迷宫</title>
<script type="text/javascript"
10步教你上传githib数据
张亚雄
git
官方的教学还有其他博客里教的都是给懂的人说得,对已我们这样对我大菜鸟只能这么来锻炼,下面先不玩什么深奥的,先暂时用着10步干净利索。等玩顺溜了再用其他的方法。
操作过程(查看本目录下有哪些文件NO.1)ls
(跳转到子目录NO.2)cd+空格+目录
(继续NO.3)ls
(匹配到子目录NO.4)cd+ 目录首写字母+tab键+(首写字母“直到你所用文件根就不再按TAB键了”)
(查看文件
MongoDB常用操作命令大全
adminjun
mongodb 操作命令
成功启动MongoDB后,再打开一个命令行窗口输入mongo,就可以进行数据库的一些操作。输入help可以看到基本操作命令,只是MongoDB没有创建数据库的命令,但有类似的命令 如:如果你想创建一个“myTest”的数据库,先运行use myTest命令,之后就做一些操作(如:db.createCollection('user')),这样就可以创建一个名叫“myTest”的数据库。
一
bat调用jar包并传入多个参数
aijuans
下面的主程序是通过eclipse写的:
1.在Main函数接收bat文件传递的参数(String[] args)
如: String ip =args[0]; String user=args[1]; &nbs
Java中对类的主动引用和被动引用
ayaoxinchao
java 主动引用 对类的引用 被动引用 类初始化
在Java代码中,有些类看上去初始化了,但其实没有。例如定义一定长度某一类型的数组,看上去数组中所有的元素已经被初始化,实际上一个都没有。对于类的初始化,虚拟机规范严格规定了只有对该类进行主动引用时,才会触发。而除此之外的所有引用方式称之为对类的被动引用,不会触发类的初始化。虚拟机规范严格地规定了有且仅有四种情况是对类的主动引用,即必须立即对类进行初始化。四种情况如下:1.遇到ne
导出数据库 提示 outfile disabled
BigBird2012
mysql
在windows控制台下,登陆mysql,备份数据库:
mysql>mysqldump -u root -p test test > D:\test.sql
使用命令 mysqldump 格式如下: mysqldump -u root -p *** DBNAME > E:\\test.sql。
注意:执行该命令的时候不要进入mysql的控制台再使用,这样会报
Javascript 中的 && 和 ||
bijian1013
JavaScript && ||
准备两个对象用于下面的讨论
var alice = {
name: "alice",
toString: function () {
return this.name;
}
}
var smith = {
name: "smith",
[Zookeeper学习笔记之四]Zookeeper Client Library会话重建
bit1129
zookeeper
为了说明问题,先来看个简单的示例代码:
package com.tom.zookeeper.book;
import com.tom.Host;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Wat
【Scala十一】Scala核心五:case模式匹配
bit1129
scala
package spark.examples.scala.grammars.caseclasses
object CaseClass_Test00 {
def simpleMatch(arg: Any) = arg match {
case v: Int => "This is an Int"
case v: (Int, String)
运维的一些面试题
yuxianhua
linux
1、Linux挂载Winodws共享文件夹
mount -t cifs //1.1.1.254/ok /var/tmp/share/ -o username=administrator,password=yourpass
或
mount -t cifs -o username=xxx,password=xxxx //1.1.1.1/a /win
Java lang包-Boolean
BrokenDreams
boolean
Boolean类是Java中基本类型boolean的包装类。这个类比较简单,直接看源代码吧。
public final class Boolean implements java.io.Serializable,
读《研磨设计模式》-代码笔记-命令模式-Command
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* GOF 在《设计模式》一书中阐述命令模式的意图:“将一个请求封装
matlab下GPU编程笔记
cherishLC
matlab
不多说,直接上代码
gpuDevice % 查看系统中的gpu,,其中的DeviceSupported会给出matlab支持的GPU个数。
g=gpuDevice(1); %会清空 GPU 1中的所有数据,,将GPU1 设为当前GPU
reset(g) %也可以清空GPU中数据。
a=1;
a=gpuArray(a); %将a从CPU移到GPU中
onGP
SVN安装过程
crabdave
SVN
SVN安装过程
subversion-1.6.12
./configure --prefix=/usr/local/subversion --with-apxs=/usr/local/apache2/bin/apxs --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr --with-openssl=/
sql 行列转换
daizj
sql 行列转换 行转列 列转行
行转列的思想是通过case when 来实现
列转行的思想是通过union all 来实现
下面具体例子:
假设有张学生成绩表(tb)如下:
Name Subject Result
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94
*/
/*
想变成
姓名 &
MySQL--主从配置
dcj3sjt126com
mysql
linux下的mysql主从配置: 说明:由于MySQL不同版本之间的(二进制日志)binlog格式可能会不一样,因此最好的搭配组合是Master的MySQL版本和Slave的版本相同或者更低, Master的版本肯定不能高于Slave版本。(版本向下兼容)
mysql1 : 192.168.100.1 //master mysq
关于yii 数据库添加新字段之后model类的修改
dcj3sjt126com
Model
rules:
array('新字段','safe','on'=>'search')
1、array('新字段', 'safe')//这个如果是要用户输入的话,要加一下,
2、array('新字段', 'numerical'),//如果是数字的话
3、array('新字段', 'length', 'max'=>100),//如果是文本
1、2、3适当的最少要加一条,新字段才会被
sublime text3 中文乱码解决
dyy_gusi
Sublime Text
sublime text3中文乱码解决
原因:缺少转换为UTF-8的插件
目的:安装ConvertToUTF8插件包
第一步:安装能自动安装插件的插件,百度“Codecs33”,然后按照步骤可以得到以下一段代码:
import urllib.request,os,hashlib; h = 'eb2297e1a458f27d836c04bb0cbaf282' + 'd0e7a30980927
概念了解:CGI,FastCGI,PHP-CGI与PHP-FPM
geeksun
PHP
CGI
CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。
CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等。 FastCGI
FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不
Git push 报错 "error: failed to push some refs to " 解决
hongtoushizi
git
Git push 报错 "error: failed to push some refs to " .
此问题出现的原因是:由于远程仓库中代码版本与本地不一致冲突导致的。
由于我在第一次git pull --rebase 代码后,准备push的时候,有别人往线上又提交了代码。所以出现此问题。
解决方案:
1: git pull
2:
第四章 Lua模块开发
jinnianshilongnian
nginx lua
在实际开发中,不可能把所有代码写到一个大而全的lua文件中,需要进行分模块开发;而且模块化是高性能Lua应用的关键。使用require第一次导入模块后,所有Nginx 进程全局共享模块的数据和代码,每个Worker进程需要时会得到此模块的一个副本(Copy-On-Write),即模块可以认为是每Worker进程共享而不是每Nginx Server共享;另外注意之前我们使用init_by_lua中初
java.lang.reflect.Proxy
liyonghui160com
1.简介
Proxy 提供用于创建动态代理类和实例的静态方法
(1)动态代理类的属性
代理类是公共的、最终的,而不是抽象的
未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留
代理类扩展 java.lang.reflect.Proxy
代理类会按同一顺序准确地实现其创建时指定的接口
Java中getResourceAsStream的用法
pda158
java
1.Java中的getResourceAsStream有以下几种: 1. Class.getResourceAsStream(String path) : path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。 2. Class.getClassLoader.get
spring 包官方下载地址(非maven)
sinnk
spring
SPRING官方网站改版后,建议都是通过 Maven和Gradle下载,对不使用Maven和Gradle开发项目的,下载就非常麻烦,下给出Spring Framework jar官方直接下载路径:
http://repo.springsource.org/libs-release-local/org/springframework/spring/
s
Oracle学习笔记(7) 开发PLSQL子程序和包
vipbooks
oracle sql 编程
哈哈,清明节放假回去了一下,真是太好了,回家的感觉真好啊!现在又开始出差之旅了,又好久没有来了,今天继续Oracle的学习!
这是第七章的学习笔记,学习完第六章的动态SQL之后,开始要学习子程序和包的使用了……,希望大家能多给俺一些支持啊!
编程时使用的工具是PLSQL