手写springv2.0aop

  • 简介
  • aop

简介

接上文,aop过程发生在ioc的实例化bean过程中。
主要类如下:

  • 代理相关:AopConfig、AdvisedSupport、AopProxy、DefaultAopProxyFactory、CglibDynamicProxy、JdkDynamicProxy
  • 切面相关:MethodInvocation、JoinPoint、Advice、MethodBeforeAdviceIntercepter、MethodAfterAdviceIntercepter、AfterReturningAdviceIntercepter、AspectJAfterThrowingAdviceIntercepter

流程如下:实例化bean时,AdvisedSupport根据配置文件获取切面配置和切点,判断当前类对象是否需要代理以及建立切点和切面的关系。DefaultAopProxyFactory根据当前类对象是否实现了接口决定通过jdk动态代理还是cglib动态代理生成代理对象,在动态代理中获得需要执行的通知链。通过MethodInvocation执行通知链,完成aop。
缺点:当前代码存在一个问题,代理对象的属性并未注入,有时间再解决吧。

aop

配置信息

#aop
#切点
pointCut=public * com.yjf.spring.demo.service..*Service.*(..)
#切面
aspectClass=com.yjf.spring.demo.aspect.LogAspect
#前置通知
aspectBefore=before
#返回通知
aspectAfterReturning=afterReturning
#异常通知
aspectAfterThrowing=afterThrowing
#异常通知名
aspectAfterThrowingName=java.lang.Exception
#后置通知
aspectAfter=after

切面

@Slf4j
public class LogAspect {//前置通知public void before(MyJoinPoint joinPoint){log.info("正在执行{}",joinPoint.getMethod());joinPoint.setUserAttribute("startTime",System.currentTimeMillis());}//返回通知public void afterReturning(MyJoinPoint joinPoint,Object returnValue){long time = System.currentTimeMillis() - (Long) joinPoint.getUserAttribute("startTime");log.info("{}执行完成,耗时{}毫秒",joinPoint.getMethod(),time);log.info("获得结果:{}",returnValue);}//异常通知public void afterThrowing(MyJoinPoint joinPoint,Throwable throwable){log.info("出现异常:{}",throwable.getMessage());}//后置通知public void after(MyJoinPoint joinPoint,Object returnValue,Throwable throwable){log.info("关闭资源");}
}

控制层

@MyController
@MyRequestMapping("aop")
public class AopController {@MyAutowiredIOneService oneService;@MyAutowiredTestService testService;@MyRequestMapping("test")public String test(){return oneService.aoptest();}@MyRequestMapping("test2")public String test2() throws Exception {return oneService.aoptest2();}@MyRequestMapping("test3")public String test3() throws Exception {testService.test();return String.valueOf(testService.flag);}
}

业务层

@MyService
public class TestService {@MyAutowiredIDemoService iDemoService;@MyAutowiredDemoService demoService;public boolean flag = true;public void test() {}
}public interface IOneService {String aoptest();String aoptest2();
}@MyService
public class OneService implements IOneService {@MyAutowiredITwoService iTwoService;@Overridepublic String aoptest() {System.out.println("aop");return "true";}@Overridepublic String aoptest2() {throw new RuntimeException("异常");}
}

上下文

public class MyApplicationContext implements MyBeanFactory {private MyBeanDefinitionReader myBeanDefinitionReader;private MyDefaultListableBeanFactory registry = new MyDefaultListableBeanFactory();private MyDefaultAopProxyFactory proxyFactory = new MyDefaultAopProxyFactory();/*** ioc容器**/private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<>();/*** 正在创建中的对象beanName* 执行创建对象流程但还未初始化完成的bean带有该标记* 不存在循环依赖的类创建对象时直接就放入一级缓存了,因此一级缓存中没有的bean且该bean存在标记时代表该类为循环依赖类**/private Set<String> singletonCurrentlyInCreation = new HashSet<>();/*** 一级缓存====保存初始化完成的bean**/private Map<String, Object> singletonObject = new HashMap<>();/*** 二级缓存====保存已创建对象但还未注入属性的bean,该容器存的都是循环依赖对象**/private Map<String, Object> earlySingletonObject = new HashMap<>();/*** 三级缓存====AOP**/private Map<String, Object> factoryBeanObjectCache = new HashMap<>();public MyApplicationContext(String configs) {//加载配置文件到工具类myBeanDefinitionReader = new MyBeanDefinitionReader(configs);//工具类解析配置文件返回配置信息List<MyBeanDefinition> myBeanDefinitions = myBeanDefinitionReader.loadBeanDefinitions();//缓存配置信息到工厂registry.doRegisterBeanDefinition(myBeanDefinitions);//加载非懒加载beandoInstance();}private void doInstance() {//获取待注册beanMap<String, MyBeanDefinition> beanDefinitionMap = this.registry.beanDefinitionMap;for (Map.Entry<String, MyBeanDefinition> stringMyBeanDefinitionEntry : beanDefinitionMap.entrySet()) {String beanName = stringMyBeanDefinitionEntry.getKey();if (!stringMyBeanDefinitionEntry.getValue().isLazyInit()) {getBean(beanName);}}}@Overridepublic Object getBean(String beanName) {Map<String, MyBeanDefinition> beanDefinitionMap = this.registry.beanDefinitionMap;//获取beanDefinitionMyBeanDefinition myBeanDefinition = beanDefinitionMap.get(beanName);if(myBeanDefinition == null) {return null;}if(null != getSingleton(beanName,myBeanDefinition)){return getSingleton(beanName,myBeanDefinition);}//标记对象创建中singletonCurrentlyInCreation.add(beanName);//反射实例化Object instance = instantiateBean(beanName, myBeanDefinition);//包装BeanMyBeanWrapper myBeanWrapper = new MyBeanWrapper(instance, instance.getClass());//依赖注入populateBean(myBeanWrapper);singletonCurrentlyInCreation.remove(beanName);singletonObject.put(beanName,instance);earlySingletonObject.remove(beanName);//保存到ioc容器中this.factoryBeanInstanceCache.put(beanName, myBeanWrapper);return myBeanWrapper.getWrappedInstance();}private Object getSingleton(String beanName, MyBeanDefinition myBeanDefinition) {//从一级缓存取beanObject singletonObject = this.singletonObject.get(beanName);//一级bean为null,存在两种情况:bean从未创建过或者bean正在创建中,通过标记容器判断if(null == singletonObject && singletonCurrentlyInCreation.contains(beanName)){//存在循环依赖//从二级缓存取beansingletonObject = earlySingletonObject.get(beanName);if(null == singletonObject){//从三级缓存取beansingletonObject = instantiateBean(beanName,myBeanDefinition);//二级缓存放入earlySingletonObject.put(beanName,singletonObject);//三级缓存移除factoryBeanObjectCache.remove(beanName);}}return singletonObject;}private void populateBean(MyBeanWrapper myBeanWrapper) {Object wrappedInstance = myBeanWrapper.getWrappedInstance();Class<?> wrappedClazz = myBeanWrapper.getWrappedClazz();//获取类对象下所有字段,根据是否存在注解@MyAutowired决定是否进行依赖注入for (Field field : wrappedClazz.getDeclaredFields()) {if (!field.isAnnotationPresent(MyAutowired.class)) {continue;}//自定义beanNameString defineBeanName = field.getAnnotation(MyAutowired.class).value();//默认以字段类型首字母小写作为beanNameString beanName = StringUtil.firstCaseToLower(field.getType().getSimpleName());//类全限定名作为beanNameString fullBeanName = field.getType().getName();//暂存默认beanNameString simpleBeanName = beanName;//标志defineBeanName是否为空boolean flag = true;if (!StringUtil.isEmpty(defineBeanName)) {beanName = defineBeanName;flag = false;}if (!flag) {if (null == getBean(beanName)) {beanName = simpleBeanName;}}if (null == getBean(beanName)) {beanName = fullBeanName;}ReflectUtil.fieldSetting(field, wrappedInstance, getBean(beanName));}}private Object instantiateBean(String beanName, MyBeanDefinition myBeanDefinition) {if(myBeanDefinition.isSingleton() && this.factoryBeanObjectCache.containsKey(beanName)){return this.factoryBeanObjectCache.get(beanName);}try {Class<?> clazz = Class.forName(myBeanDefinition.getBeanClassName());Object instance = clazz.newInstance();//如果是代理对象,触发AOP的逻辑//==================AOP开始=========================//如果满足条件,就直接返回Proxy对象//1、加载AOP的配置文件MyAdvisedSupport config = instantionAopConfig(myBeanDefinition);config.setTargetClass(clazz);config.setTarget(instance);//判断规则,要不要生成代理类,如果要就覆盖原生对象//如果不要就不做任何处理,返回原生对象if(config.pointCutMath()){instance = proxyFactory.createAopProxy(config).getProxy();}if(myBeanDefinition.isSingleton()){if(!StringUtil.firstCaseToLower(Class.forName(myBeanDefinition.getBeanClassName()).getSimpleName()).equals(beanName)){this.factoryBeanObjectCache.put(StringUtil.firstCaseToLower(Class.forName(myBeanDefinition.getBeanClassName()).getSimpleName()), instance);}this.factoryBeanObjectCache.put(beanName, instance);this.factoryBeanObjectCache.put(myBeanDefinition.getBeanClassName(), instance);}return instance;} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic Object getBean(Class clazz) {return getBean(clazz.getName());}public int getBeanDefinitionCount() {return this.registry.beanDefinitionMap.size();}public String[] getBeanDefinitionNames() {return this.registry.beanDefinitionMap.keySet().toArray(new String[]{});}public MyBeanDefinition getBeanDefinitionByName(String beanName) {return this.registry.beanDefinitionMap.get(beanName);}public Properties getConfig(){return this.myBeanDefinitionReader.getConfig();}private MyAdvisedSupport instantionAopConfig(MyBeanDefinition beanDefinition) {MyAopConfig config = new MyAopConfig();config.setPointCut(this.myBeanDefinitionReader.getConfig().getProperty("pointCut"));config.setAspectClass(this.myBeanDefinitionReader.getConfig().getProperty("aspectClass"));config.setAspectBefore(this.myBeanDefinitionReader.getConfig().getProperty("aspectBefore"));config.setAspectAfterReturning(this.myBeanDefinitionReader.getConfig().getProperty("aspectAfterReturning"));config.setAspectAfterThrowing(this.myBeanDefinitionReader.getConfig().getProperty("aspectAfterThrowing"));config.setAspectAfterThrowingName(this.myBeanDefinitionReader.getConfig().getProperty("aspectAfterThrowingName"));config.setAspectAfter(this.myBeanDefinitionReader.getConfig().getProperty("aspectAfter"));return new MyAdvisedSupport(config);}
}

切面配置信息

@Data
public class MyAopConfig {private String pointCut;private String aspectClass;private String aspectBefore;private String aspectAfterReturning;private String aspectAfterThrowing;private String aspectAfterThrowingName;private String aspectAfter;
}

解析配置信息并且建立切点和切面的关系

public class MyAdvisedSupport {private MyAopConfig aopConfig;//原生beanprivate Object target;//原生bean的类对象private Class targetClass;//切点所在的类 com.demo.example..*Serviceprivate Pattern pointCutClassPattern;//键:切点  值:通知集合private Map<Method, List<Object>> methodCache = new HashMap<>();public MyAdvisedSupport(MyAopConfig aopConfig) {this.aopConfig = aopConfig;}/*** 解析配置信息并且建立切点和切面的关系** @param* @return void**/public void parse(){//切面表达式转成正则表达式String pointCut = aopConfig.getPointCut()//*替换成\w+.replaceAll("\\*", "\\\\w\\+")//.替换成\..replaceAll("\\.", "\\\\.")//\.\.替换成.*.replaceAll("(\\\\.)\\1", "\\.\\*")//(替换成\(.replaceAll("\\(", "\\\\(")//)替换成\).replaceAll("\\)", "\\\\)")//\w+替换成.*?.replaceAll("\\\\w\\+", "\\.\\*\\?");//匹配方法的正则表达式Pattern pointCutPattern = Pattern.compile(pointCut);//截取切面表达式所在的类全限定名int end = pointCut.substring(0, pointCut.indexOf("\\(")).lastIndexOf("\\.");pointCut = pointCut.substring(0, end);pointCut = pointCut.substring(pointCut.lastIndexOf(" ") + 1);//匹配类的正则表达式pointCutClassPattern = Pattern.compile("class " + pointCut);if(!pointCutMath()){return;}try {//切面方法名和方法Map<String, Method> aspectMethods = new HashMap<>();for (Method method : Class.forName(aopConfig.getAspectClass()).getDeclaredMethods()) {aspectMethods.put(method.getName(), method);}List<Object> advices;//切面类实例Object aspectObject = Class.forName(aopConfig.getAspectClass()).newInstance();//遍历目标类,判断方法是否匹配切点for (Method m : targetClass.getMethods()) {String methodString = m.toString();if (methodString.contains("throws")) {methodString = methodString.substring(0, methodString.indexOf("throws")).trim();}//该方法与切点匹配advices = new LinkedList<>();if (pointCutPattern.matcher(methodString).matches()) {if (!StringUtil.isEmpty(aopConfig.getAspectBefore())) {advices.add(new MyMethodBeforeAdviceIntercepter(aspectObject,aspectMethods.get(aopConfig.getAspectBefore())));}if (!StringUtil.isEmpty(aopConfig.getAspectAfterReturning())) {advices.add(new MyAfterReturningAdviceIntercepter(aspectObject,aspectMethods.get(aopConfig.getAspectAfterReturning())));}if (!StringUtil.isEmpty(aopConfig.getAspectAfterThrowing())) {MyAspectJAfterThrowingAdviceIntercepter myAspectJAfterThrowingAdviceIntercepter = new MyAspectJAfterThrowingAdviceIntercepter(aspectObject, aspectMethods.get(aopConfig.getAspectAfterThrowing()));myAspectJAfterThrowingAdviceIntercepter.setThrowsExceptionName(aopConfig.getAspectAfterThrowingName());advices.add(myAspectJAfterThrowingAdviceIntercepter);}if (!StringUtil.isEmpty(aopConfig.getAspectAfter())) {advices.add(new MyMethodAfterAdviceIntercepter(aspectObject,aspectMethods.get(aopConfig.getAspectAfter())));}methodCache.put(m, advices);}}} catch (Exception e) {e.printStackTrace();}}//判断全限定名是否匹配public boolean pointCutMath() {return pointCutClassPattern.matcher(this.targetClass.toString()).matches();}public Object getTarget() {return target;}public void setTarget(Object target) {this.target = target;}public Class getTargetClass() {return targetClass;}public void setTargetClass(Class targetClass) {this.targetClass = targetClass;parse();}/*** 从缓存中取出该切点对应的通知集合** @param method  接口方法或父类方法或原生bean方法* @param targetClass 原生bean类对象* @return  java.util.List<java.lang.Object>**/public List<Object> getInterceptersAndDynamicInterceptionAdvice(Method method,Class targetClass) throws NoSuchMethodException {// 从缓存中获取List<Object> objects = methodCache.get(method);//当method是接口方法时,第一次从缓存中是取不到的if(null == objects){Method m = targetClass.getDeclaredMethod(method.getName(),method.getParameterTypes());objects = methodCache.get(m);methodCache.put(method,objects);}return objects;}
}

AopProxy

public interface MyAopProxy {Object getProxy();Object getProxy(ClassLoader classLoader);
}

代理工厂,选择代理方式

public class MyDefaultAopProxyFactory{public MyAopProxy createAopProxy(MyAdvisedSupport advisedSupport){if(advisedSupport.getTargetClass().getInterfaces().length > 0){return new MyJdkDynamicProxy(advisedSupport);}else {return new MyCglibDynamicProxy(advisedSupport);}}
}

jdk动态代理

public class MyJdkDynamicProxy implements MyAopProxy, InvocationHandler {private MyAdvisedSupport config;public MyJdkDynamicProxy(MyAdvisedSupport config) {this.config = config;}@Overridepublic Object getProxy() {return getProxy(this.getClass().getClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {return Proxy.newProxyInstance(classLoader,config.getTargetClass().getInterfaces(),this);}@Override/*** 代理方法  代理对象中调用h.invoke(..)** @param proxy 代理对象* @param method 原方法  ===接口方法* @param args 实参* @return  java.lang.Object**/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {List<Object> chain = config.getInterceptersAndDynamicInterceptionAdvice(method, config.getTargetClass());MyMethodInvocation mi = new MyMethodInvocation(proxy,config.getTarget(),method,args,config.getTargetClass(),chain);return mi.proceed();}
}

cglib动态代理

public class MyCglibDynamicProxy implements MyAopProxy, MethodInterceptor {private MyAdvisedSupport advisedSupport;public MyCglibDynamicProxy(MyAdvisedSupport advisedSupport) {this.advisedSupport = advisedSupport;}@Overridepublic Object getProxy() {return getProxy(this.getClass().getClassLoader());}@Overridepublic Object getProxy(ClassLoader classLoader) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.advisedSupport.getTargetClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {List<Object> chain = advisedSupport.getInterceptersAndDynamicInterceptionAdvice(method, null);MyMethodInvocation mi = new MyMethodInvocation(proxy,advisedSupport.getTarget(),method,args,advisedSupport.getTargetClass(),chain);return mi.proceed();}
}

连接点

public interface MyJoinPoint {/*** jdk:接口实现类的原生bean对象* cglib : null** @param* @return  java.lang.Object**/Object getThis();/*** jdk:接口中的方法* cglib: 父类的方法** @param* @return  java.lang.reflect.Method**/Method getMethod();/*** jdk:接口方法中的参数* cglib:父类的方法中的参数** @param* @return  java.lang.Object[]**/Object[] getArguments();/*** 设置属性** @param key* @param value* @return  void**/void setUserAttribute(String key,Object value);/*** 获取属性** @param key* @return  java.lang.Object**/Object getUserAttribute(String key);}

连接点实现,贯穿整个代理过程,动态代理调用该对象,该对象提供方法增强过程

public class MyMethodInvocation implements MyJoinPoint {private final Object proxy;private final Object target;private final Class<?> targetClass;private final Method method;private final Object[] arguments;//通知集合private final List<Object> interceptorsAndDynamicMethodMatchers;//连接点属性private Map<String,Object> userAttributers = new HashMap<>();//通知执行索引private int currentIntercepterIndex = -1;public MyMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {this.proxy = proxy;this.target = target;this.targetClass = targetClass;this.method = method;this.arguments = arguments;this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;}/*** 执行代理过程** @param* @return  java.lang.Object**/public Object proceed() throws Throwable{if(currentIntercepterIndex == interceptorsAndDynamicMethodMatchers.size() - 2){return method.invoke(target,arguments);}else {Object advice = interceptorsAndDynamicMethodMatchers.get(++currentIntercepterIndex);if(advice instanceof MyMethodIntercepter){return ((MyMethodIntercepter) advice).invoke(this);}else {return proceed();}}}@Overridepublic Object getThis() {return target;}@Overridepublic Method getMethod() {return method;}@Overridepublic Object[] getArguments() {return arguments;}@Overridepublic void setUserAttribute(String key, Object value) {userAttributers.put(key,value);}@Overridepublic Object getUserAttribute(String key) {return userAttributers.get(key);}public List<Object> getInterceptorsAndDynamicMethodMatchers() {return interceptorsAndDynamicMethodMatchers;}
}

方法拦截

public interface MyMethodIntercepter {Object invoke(MyMethodInvocation methodInvocation) throws Throwable;
}

通知

public interface MyAdvice {}

抽象通知,提供通知类公共方法

public abstract class MyAbstractAspectJAdvice implements MyAdvice{//切面对象private Object aspectObject;//切面通知private Method aspectMethod;public MyAbstractAspectJAdvice(Object aspectObject,Method aspectMethod){this.aspectObject = aspectObject;this.aspectMethod = aspectMethod;}//调用通知方法protected Object invokeAdviceMethod(MyJoinPoint joinPoint, Object returnValue, Throwable ex)throws Throwable {Class<?> [] paramTypes = this.aspectMethod.getParameterTypes();if(null == paramTypes || paramTypes.length == 0){return this.aspectMethod.invoke(aspectObject);}else {Object[] args = new Object[paramTypes.length];for (int i = 0; i < paramTypes.length; i++) {if (paramTypes[i] == MyJoinPoint.class) {args[i] = joinPoint;} else if (paramTypes[i] == Throwable.class) {args[i] = ex;} else if (paramTypes[i] == Object.class) {args[i] = returnValue;}}return this.aspectMethod.invoke(aspectObject, args);}}
}

前置通知

public class MyMethodBeforeAdviceIntercepter extends MyAbstractAspectJAdvice implements MyMethodIntercepter {private MyJoinPoint jp;public MyMethodBeforeAdviceIntercepter(Object aspectObject, Method aspectMethod) {super(aspectObject, aspectMethod);}public void before(Method method, Object[] arguments, Object aThis) throws Throwable{invokeAdviceMethod(this.jp,null,null);}@Overridepublic Object invoke(MyMethodInvocation methodInvocation) throws Throwable {//执行前置通知jp = methodInvocation;Object proceed = null;before(methodInvocation.getMethod(),methodInvocation.getArguments(),methodInvocation.getThis());try{proceed = methodInvocation.proceed();}catch (Throwable tx){executeLastAdvice(methodInvocation);throw tx;}executeLastAdvice(methodInvocation);return proceed;}private void executeLastAdvice(MyMethodInvocation methodInvocation) throws Throwable {//执行后置通知Object finallyAdvice = methodInvocation.getInterceptorsAndDynamicMethodMatchers().get(methodInvocation.getInterceptorsAndDynamicMethodMatchers().size() - 1);((MyMethodAfterAdviceIntercepter)finallyAdvice).invoke(methodInvocation);}
}

返回通知

public class MyAfterReturningAdviceIntercepter extends MyAbstractAspectJAdvice implements MyMethodIntercepter {private MyJoinPoint jp;public MyAfterReturningAdviceIntercepter(Object aspectObject, Method aspectMethod) {super(aspectObject, aspectMethod);}private void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable{this.invokeAdviceMethod(this.jp,returnValue,null);}@Overridepublic Object invoke(MyMethodInvocation methodInvocation) throws Throwable {jp = methodInvocation;Object res = methodInvocation.proceed();//执行返回通知afterReturning(res, methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());return res;}
}

异常通知

public class MyAspectJAfterThrowingAdviceIntercepter extends MyAbstractAspectJAdvice implements MyMethodIntercepter {public void setThrowsExceptionName(String throwsExceptionName) {this.throwsExceptionName = throwsExceptionName;}private String throwsExceptionName;public MyAspectJAfterThrowingAdviceIntercepter(Object aspectObject, Method aspectMethod) {super(aspectObject, aspectMethod);}@Overridepublic Object invoke(MyMethodInvocation methodInvocation) throws Throwable {try{return methodInvocation.proceed();}catch (Throwable tx){//异常通知invokeAdviceMethod(methodInvocation,null,tx);throw tx;}}
}

后置通知

public class MyMethodAfterAdviceIntercepter extends MyAbstractAspectJAdvice implements MyMethodIntercepter {private MyJoinPoint jp;public MyMethodAfterAdviceIntercepter(Object aspectObject, Method aspectMethod) {super(aspectObject, aspectMethod);}public void after(Method method, Object[] arguments, Object aThis) throws Throwable{invokeAdviceMethod(this.jp,null,null);}@Overridepublic Object invoke(MyMethodInvocation methodInvocation) throws Throwable {//执行后置通知jp = methodInvocation;after(methodInvocation.getMethod(),methodInvocation.getArguments(),methodInvocation.getThis());return null;}
}

架构师内功心法-----手写springv2.0aop相关推荐

  1. 架构师内功心法-----手写springv2.0mvc

    手写springv2.0ioc与di 简介 mvc 简介 springmvc中有九大组件 MultipleResolver. LocalResolver.ThemeResolver.HandlerMa ...

  2. 记录一次阿里架构师全程手写Spring MVC

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  3. 高级架构师纯手写MySQL笔记,已经熬夜学习了

    数据库是一个综合系统,其背后是发展了几十年的数据库理论.也许你会觉得数据库并不难,因为你可以熟练地写出SQL,也可以在各个客户端里玩得游刃有余.但就以最常见的MySQL为例,作为程序员,你在使用MyS ...

  4. 架构师内功心法之设计原则

    一.架构师内功心法之设计原则 1.为什么要学习软件架构设计原则 1.1.课程目标 通过对节课内容的学习,了解设计原则的重要性. 掌握七大设计原则的具体内容. 1.2.内容定位 学习设计原则,学习设计模 ...

  5. 为什么 CTO、技术总监、架构师都不写代码,还这么牛?

    作者| Mr.K   整理| Emma 来源| 技术领导力(ID:jishulingdaoli) 常常会被问到这样的问题:CTO.技术总监.架构师很少写具体代码,为什么还很牛逼的样子,拿这么高工资? ...

  6. CTO不写代码就算了,架构师也不写?

    从什么时候起,技术角色的提升就意味着脱离技术与交付?CTO 不写代码已经引起诸多争议了,架构师也不写代码,能行吗? 当我面试架构师职位的候选人时,我通常会问一个这样的问题:"你认为架构师是否 ...

  7. 为什么CTO、总监、架构师都不写代码,还这么牛逼?

    见字如面,我是军哥! 常常会被问到这样的问题:CTO.技术总监.架构师很少写具体代码,为什么还很牛逼的样子,拿这么高工资? 其实,这个问题本身就错了.就好比问:导演.制片人为什么不懂演戏,还能指导演员 ...

  8. 为什么CTO、技术总监、架构师都不写代码,还这么牛逼?

    常常会被问到这样的问题:CTO.技术总监.架构师很少写具体代码,为什么还很牛逼的样子,拿这么高工资? 其实,这个问题本身就错了.就好比问:导演.制片人为什么不懂演戏,还能指导演员,好像比演员厉害似的? ...

  9. 架构师之路-写的不错

    架构师之路 转:http://www.uml.org.cn/zjjs/200903273.asp 1.引言 机算机科学是一门应用科学,它的知识体系是典型的倒三角结构,所用的基础知识并不多,只是随着应用 ...

最新文章

  1. Java编码技巧之高效代码50例
  2. Mac 安装 MySQL-python 问题解决
  3. 超经典的存储过程分页 ;-) 引自CSDN网友所作
  4. 周志华《机器学习》课后习题(第三章):线性模型
  5. C学习杂记(五)形参实参笔试题
  6. Unity3D核心类型一览
  7. miniUI mini-monthpicker ie8兼容性问题
  8. 我是 LinkedIn 的 SRE ,我把 LinkedIn 搞挂了
  9. flutter 防止键盘弹出 导致超出屏幕
  10. .netcore 2.0 mysql_搭建连接MySql的三层架构的ASP.NetCore2.0的WebApi
  11. JQuery中关于html()、text()、val()三者之间的区别
  12. mybaitis快速生成_使用MyBatis插件生成代码
  13. 计算机安装系统说明,电脑操作系统安装方法-详细图解说明-简单安装Windows系统...
  14. 高斯核与高斯核的卷积的结果还是一个高斯核的推导
  15. 定义多边形类,继承产生矩形类和正多边形类
  16. 重装系统后计算机无法启动,重装系统后电脑为什么启动不了?云骑士告诉你怎么办?...
  17. 疯狂的二手电商:爱回收偷食闲鱼、转转
  18. Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解
  19. SpringMVC优雅的实现数据校验
  20. excel数据透视表数据排序及excel表格转成word表

热门文章

  1. 超甜来袭!治愈系数字藏品限时免费,速抢!
  2. java算法题——分数约分
  3. MySQL OCP考试记(r5笔记第32天)
  4. CEGUI订阅者模式
  5. Win10 x64 1903 搭建双机调试
  6. 软文的写作四大策略及技巧
  7. html怎么在链接中加链接,怎么给div加链接 在div中可以加整体的链接
  8. 本地存储(localStorage)
  9. python案例解析_python案例讲解
  10. C# 查询注册表,判断本机是否安装Office2003,2007,2010,2013,2016和WPS