spring源码-aop源码-5.1
一、aop的源码部分还是有点复杂的,但是为了更好的理解,我这里会省去很多不必要的逻辑实现过程。主要方向还是更好的理解整体代码的实现过程。
二、说明重点:aop的过程主要过程有两点:第一点,发现正确和适配的过程。第二点就是动态代理
三、源码部分
1)可能开始有点奇怪哈,从哪里下手呢?spring所有的东西还是基于配置来实现的,虽然后面修改了很多方式比如注解。但是我们这里还是从注解出发。
<aop:aspectj-autoproxy/>
说明:这个注解也算是spring的自定义注解吧,通过spring源码-自定义标签-4的解读我们可以知道解析是从NamespaceHandlerSupport的实现类开始的。
2)通过需要我们可以发现aop的NamespaceHandlerSupport的实现类为AopNamespaceHandler
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
}
public void init() {
this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
//这里就是自动解析过程
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
说明:<aop:aspectj-autoproxy/>的解析过程就是在AspectJAutoProxyBeanDefinitionParser下面进行解析的。
3)AspectJAutoProxyBeanDefinitionParser
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
AspectJAutoProxyBeanDefinitionParser() {
}
//解析过程,不多解释。不懂可以开自定义标签的解析方式
public BeanDefinition parse(Element element, ParserContext parserContext) {
//这里就是整个代理的过程了
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
//这里不做太多讲解,也没有写出来
this.extendBeanDefinition(element, parserContext);
return null;
}
......
}
4)registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
//注册过程:名为org.springframework.aop.config.internalAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//解析proxy-target-class(设置代理模式默认false, true为cglib),expose-proxy(处理内部增强,可以自己百度)
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
5)registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//如果存在org.springframework.aop.config.internalAutoProxyCreator直接使用
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {
//没有就自己注册
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
6)注册到容器的过程就结束了,接下来就是看AnnotationAwareAspectJAutoProxyCreator.class有什么不一样了

通过上面我们可以知道,bean的后置操作。肯定是做了什么!
7)BeanPostProcessor的后置操作(不明白可以参考:spring源码-BeanPostProcessor-3.3)
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
//通过自己封装的key获取(beanClassName_beanName)
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
//缓存中不包含
if (!this.earlyProxyReferences.contains(cacheKey)) {
//解析过程
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
8)wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (this.nonAdvisedBeans.contains(cacheKey)) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
//获取增强器
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.add(cacheKey);
//创建代理
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
//返回代理过后的bean,加入容器
return proxy;
} else {
this.nonAdvisedBeans.add(cacheKey);
return bean;
}
} else {
this.nonAdvisedBeans.add(cacheKey);
return bean;
}
}
说明:增强器可以理解成对需要代理的类的增强,代理过程就是对需要的代理类进行相关处理,后续我会在动态代理讲解。
9)getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
//发现合适的增强器
List advisors = this.findEligibleAdvisors(beanClass, beanName);
return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
//查找所有增强器
List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
//发现合适的增强器
List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
this.extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
9.1)findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
//发现父类是否存在
List<Advisor> advisors = super.findCandidateAdvisors();
//添加新发现的
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized(this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList();
List<String> aspectNames = new LinkedList();
//获取所有beanNames
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
String[] var8 = beanNames;
int var7 = beanNames.length;
for(int var18 = 0; var18 < var7; ++var18) {
String beanName = var8[var18];
if (this.isEligibleBean(beanName)) {
Class beanType = this.beanFactory.getType(beanName);
//判断是否是Aspect类型(不做讲解了,就是注解的判断过程)
if (beanType != null && this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
//单例
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//获取增强器
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
} else {
this.aspectFactoryCache.put(beanName, factory);
}
//加入list返回
advisors.addAll(classAdvisors);
} else {
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
}
//缓存名称
this.aspectBeanNames = aspectNames;
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.EMPTY_LIST;
} else {
List<Advisor> advisors = new LinkedList();
Iterator var4 = aspectNames.iterator();
while(var4.hasNext()) {
String aspectName = (String)var4.next();
List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
} else {
MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
}
9.2)getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
final String aspectName = maaif.getAspectMetadata().getAspectName();
this.validate(aspectClass);
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(maaif);
final List<Advisor> advisors = new LinkedList();
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
//循环获取增强器
Advisor advisor = ReflectiveAspectJAdvisorFactory.this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
}
});
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
Field[] var9;
int var8 = (var9 = aspectClass.getDeclaredFields()).length;
for(int var7 = 0; var7 < var8; ++var7) {
Field field = var9[var7];
Advisor advisor = this.getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
this.validate(aif.getAspectMetadata().getAspectClass());
//获取切点
AspectJExpressionPointcut ajexp = this.getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
//是计划增强器
return ajexp == null ? null : new InstantiationModelAwarePointcutAdvisorImpl(this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}
9.3)getPointcut、InstantiationModelAwarePointcutAdvisorImpl
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
//发现注解的过程
AbstractAspectJAdvisorFactory.AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} else {
//处理表达式(不讲解了)
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
return ajexp;
}
}
protected static AbstractAspectJAdvisorFactory.AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
//默认的集中注解
Class[] classesToLookFor = new Class[]{Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
Class[] var5 = classesToLookFor;
int var4 = classesToLookFor.length;
for(int var3 = 0; var3 < var4; ++var3) {
Class<? extends Annotation> c = var5[var3];
//寻找过程(通过方法的注解判断,有兴趣自己看一下)
AbstractAspectJAdvisorFactory.AspectJAnnotation foundAnnotation = findAnnotation(method, c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
this.declaredPointcut = ajexp;
this.method = method;
this.atAspectJAdvisorFactory = af;
this.aspectInstanceFactory = aif;
this.declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;
//判断是否是lazy
if (aif.getAspectMetadata().isLazilyInstantiated()) {
Pointcut preInstantiationPointcut = Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif, (InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut)null);
this.lazy = true;
} else {
//根据默认类型获取增强器
this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
this.pointcut = this.declaredPointcut;
this.lazy = false;
}
}
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
//获取增强器(具体表达式的应用)
return this.atAspectJAdvisorFactory.getAdvice(this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
this.validate(candidateAspectClass);
AbstractAspectJAdvisorFactory.AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} else if (!this.isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
Object springAdvice;
switch($SWITCH_TABLE$org$springframework$aop$aspectj$annotation$AbstractAspectJAdvisorFactory$AspectJAnnotationType()[aspectJAnnotation.getAnnotationType().ordinal()]) {
case 1:
if (this.logger.isDebugEnabled()) {
this.logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case 2:
//前置
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case 3:
//后置
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case 4:
//return
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
}
break;
case 5:
//异常
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case 6:
//环绕
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
default:
throw new UnsupportedOperationException("Unsupported advice type on method " + candidateAdviceMethod);
}
((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrderInAspect);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
}
((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
return (Advice)springAdvice;
}
}
备注:这就是整个发现增强器的过程,过程有点多。但是基本流程就是这样子的了。增强器后续会单独讲解
说明:匹配过程就不讲解了,主要是通过表达式进行匹配
10)createProxy
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
int var8;
int var9;
if (!this.shouldProxyTargetClass(beanClass, beanName)) {
Class[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
Class[] var10 = targetInterfaces;
var9 = targetInterfaces.length;
for(var8 = 0; var8 < var9; ++var8) {
Class<?> targetInterface = var10[var8];
proxyFactory.addInterface(targetInterface);
}
}
//家里增强器
Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
Advisor[] var11 = advisors;
var9 = advisors.length;
for(var8 = 0; var8 < var9; ++var8) {
Advisor advisor = var11[var8];
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
this.customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (this.advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//创建代理
return proxyFactory.getProxy(this.proxyClassLoader);
}
public Object getProxy(ClassLoader classLoader) {
return this.createAopProxy().getProxy(classLoader);
}
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
this.activate();
}
return this.getAopProxyFactory().createAopProxy(this);
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
//默认JDK动态代理
return new JdkDynamicAopProxy(config);
} else {
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.");
} else if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
} else if (!cglibAvailable) {
throw new AopConfigException("Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.");
} else {
//cglib
return DefaultAopProxyFactory.CglibProxyFactory.createCglibProxy(config);
}
}
}
说明:代理过程就是讲具体的增强加入到代理中去。这里动态代理后续会讲,不详细介绍。
11)springaop的源码过程却是有些复杂。但是思路已经有了哦
发现增强-->匹配增强器-->创建代理
spring源码-aop源码-5.1的更多相关文章
- Spring框架之AOP源码完全解析
Spring框架之AOP源码完全解析 Spring可以说是Java企业开发里最重要的技术.Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Orie ...
- Spring基础系列-AOP源码分析
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...
- Spring框架之jms源码完全解析
Spring框架之jms源码完全解析 我们在前两篇文章中介绍了Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmi ...
- Spring框架之spring-webmvc源码完全解析
Spring框架之spring-webmvc源码完全解析 Spring框架提供了构建Web应用程序的全功能MVC模块.Spring MVC分离了控制器.模型对象.分派器以及处理程序对象的角色,支持多种 ...
- Spring框架之jdbc源码完全解析
Spring框架之jdbc源码完全解析 Spring JDBC抽象框架所带来的价值将在以下几个方面得以体现: 1.指定数据库连接参数 2.打开数据库连接 3.声明SQL语句 4.预编译并执行SQL语句 ...
- Spring框架之websocket源码完全解析
Spring框架之websocket源码完全解析 Spring框架从4.0版开始支持WebSocket,先简单介绍WebSocket协议(详细介绍参见"WebSocket协议中文版" ...
- Spring框架之事务源码完全解析
Spring框架之事务源码完全解析 事务的定义及特性: 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一 ...
- Spring基础系列--AOP织入逻辑跟踪
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...
- Spring核心框架 - AOP的原理及源码解析
一.AOP的体系结构 如下图所示:(引自AOP联盟) 层次3语言和开发环境:基础是指待增加对象或者目标对象:切面通常包括对于基础的增加应用:配置是指AOP体系中提供的配置环境或者编织配置,通过该配置A ...
随机推荐
- MySQL 事务没有提交导致 锁等待 Lock wait timeout exceeded
java.lang.Exception: ### Error updating database. Cause: java.sql.SQLException: Lock wait timeout e ...
- 《metasploit渗透测试魔鬼训练营》学习笔记第九章--meterpreter
七.强大的meterpreter 7.1再探metasploit的攻击载荷模块 7.1.1典型的攻击载荷模块 metasploit涵盖了各大主流操作系统和平台,其中绝大部分是远程漏洞 ...
- iOS沙盒目录文件操作
简介 沙盒(NSHomeDirectory())中总共有四个文件夹,documents.tmp.app.Library; 手动保存的文件在documents文件里; Nsuserdefaults保存的 ...
- Spring(一)之概括与架构
个人说明: 下面有一部分引用该链接:https://www.tutorialspoint.com/spring/spring_architecture.htm 另外一部分加上我个人的使用经验和体会 之 ...
- PAT——1061. 判断题
判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过100的正整数N和M,分别是学生人数和判断题数量.第二行给出M个不超过5的正 ...
- oracle11g之Oracle体系结构(理论基础知识)
第二章 oracle的体系结构 一.oracle体系结构概述1.实例和数据库组成完整的Oracle数据库系统数据库:一系列物理文件的集合(数据文件,控制文件,联机日志,参数文件等)实例:一组oracl ...
- vue02—— 动画、组件、组件之间的数据通信
一.vue中使用动画 文档:https://cn.vuejs.org/v2/guide/transitions.html 1. Vue 中的过渡动画 <!DOCTYPE html> < ...
- Notes 20180507 : Java程序设计之环境搭建与HelloWord
3 HelloWorld 不管从事什么工作那么一个工作环境总是必不可少的,那怕你只是要写篇文章,一张平坦的书桌和流利的书写笔总是能帮助我们完成工作的,Java开发更是如此.在开始今天的HelloWor ...
- multiprocessing进程开发RuntimeError
windows环境下multiprocessing报如下异常信息: RuntimeError: An attempt has been made to start a new process befo ...
- Vue聊天框默认滚动到底部
功能场景 在开发中,我们总能遇到某些场景需要运用到聊天框,比如客服对话.如果你不是一名开发人员,可能你在使用QQ或者聊天工具的时候并没有注意到,当你发出一条消息的时候,窗体会默认滚动到最底部,让用户可 ...