一.手写ioc前基础知识

1.什么是IOC(Inversion of Control 控制反转)?

IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

2.什么是DI(Dependency Injection 依赖注入)?

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

3.IOC和DI什么关系?

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

4.什么是依赖?

传统应用程序设计中所说的依赖一般指“类之间的关系”,那先让我们复习一下类之间的关系:

泛化:表示类与类之间的继承关系、接口与接口之间的继承关系;

实现:表示类对接口的实现;

依赖:当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种“相识关系”,只在某个特定地方(比如某个方法体内)才有关系。

关联:表示类与类或类与接口之间的依赖关系,表现为“拥有关系”;具体到代码可以用实例变量来表示;

聚合:属于是关联的特殊情况,体现部分-整体关系,是一种弱拥有关系;整体和部分可以有不一样的生命周期;是一种弱关联;

组合:属于是关联的特殊情况,也体现了体现部分-整体关系,是一种强“拥有关系”;整体与部分有相同的生命周期,是一种强关联;

Spring IoC容器的依赖有两层含义:Bean依赖容器和容器注入Bean的依赖资源:

Bean依赖容器:也就是说Bean要依赖于容器,这里的依赖是指容器负责创建Bean并管理Bean的生命周期,正是由于由容器来控制创建Bean并注入依赖,也就是控制权被反转了,这也正是IoC名字的由来,此处的有依赖是指Bean和容器之间的依赖关系。

容器注入Bean的依赖资源:容器负责注入Bean的依赖资源,依赖资源可以是Bean、外部文件、常量数据等,在Java中都反映为对象,并且由容器负责组装Bean之间的依赖关系,此处的依赖是指Bean之间的依赖关系,可以认为是传统类与类之间的“关联”、“聚合”、“组合”关系。

5.依赖注入的好处?

动态替换Bean依赖对象,程序更灵活:替换Bean依赖对象,无需修改源文件:应用依赖注入后,由于可以采用配置文件方式实现,从而能随时动态的替换Bean的依赖对象,无需修改java源文件;

更好实践面向接口编程,代码更清晰:在Bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现;

更好实践优先使用对象组合,而不是类继承:因为IoC容器采用注入依赖,也就是组合对象,从而更好的实践对象组合。

  • 采用对象组合,Bean的功能可能由几个依赖Bean的功能组合而成,其Bean本身可能只提供少许功能或根本无任何功能,全部委托给依赖Bean,对象组合具有动态性,能更方便的替换掉依赖Bean,从而改变Bean功能;

  • 而如果采用类继承,Bean没有依赖Bean,而是采用继承方式添加新功能,,而且功能是在编译时就确定了,不具有动态性,而且采用类继承导致Bean与子Bean之间高度耦合,难以复用。

    增加Bean可复用性:依赖于对象组合,Bean更可复用且复用更简单;

    降低Bean之间耦合:由于我们完全采用面向接口编程,在代码中没有直接引用Bean依赖实现,全部引用接口,而且不会出现显示的创建依赖对象代码,而且这些依赖是由容器来注入,很容易替换依赖实现类,从而降低Bean与依赖之间耦合;

    代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。

从以上我们可以看出,其实依赖注入只是一种装配对象的手段,设计的类结构才是基础,如果设计的类结构不支持依赖注入,Spring IoC容器也注入不了任何东西,从而从根本上说如何设计好类结构才是关键,依赖注入只是一种装配对象手段”。

二. 手写IOC

标记配置分为集中式管理和分散式管理,即xml文件方式和注解方式配置元数据信息,手写ioc我采用注解配置元数据方式来实现ioc的大致运行过程。

1.定义元数据配置信息

@Component/@Controller/@Repository@Service

扫描什么样的class装载到ioc容器中进行管理。

@Autowired

什么样的对象进行依赖注入。

/**
* @Classname Component
* @Description
* @Date 2020/8/6 14:54
* @Created by zhangtianci
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
/**
* @Classname Controller
* @Description TODO
* @Date 2020/8/6 14:56
* @Created by zhangtianci
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
/**
* @Classname Repository
* @Description TODO
* @Date 2020/8/6 14:58
* @Created by zhangtianci
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}
/**
* @Classname Service
* @Description TODO
* @Date 2020/8/6 14:57
* @Created by zhangtianci
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}
/**
* @Classname Autowired
* @Description 自动注入注解
* @Date 2020/8/6 14:28
* @Created by zhangtianci
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value() default "";
}

2.实现ioc容器

核心功能:加载被配置标记的class文件,交给ioc容器管理。

package org.simplespring.core;

import lombok.extern.slf4j.Slf4j;
import org.simplespring.core.annotation.Component;
import org.simplespring.core.annotation.Controller;
import org.simplespring.core.annotation.Repository;
import org.simplespring.core.annotation.Service;
import org.simplespring.util.ClassUtil;
import org.simplespring.util.ValidationUtil; import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap; /**
* @Classname BeanContainer
* @Description bean容器
* <p>
* BeanContainer应该是单例的 采用内部枚举的方式实现。
*
* 应该拥有的实例方法:
* 1.boolean isLoad() 是否加载
* 2.int getSize() 获取bean个数
* 3.loadBeans(String packageName) 根据包名加载所有被配置标记的class文件/
* 4.获取/删除/增加bean及提供一些便利的方法
*
* @Date 2020/8/6 14:41
* @Created by zhangtianci
*/
@Slf4j
public class BeanContainer {
/**
* 存放所有被配置标记(xml/注解)的class,并new出一个实例对象bean
* 存放在一个map中
*/
private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>(); /**
* 加载bean的注解列表
*/
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class, Service.class, Repository.class); /**
* 是否已经加载过bean
*/
private boolean loaded = false; /**
* 获取bean容器
*
* @return
*/
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
} private enum ContainerHolder {
HOLDER; private BeanContainer instance; ContainerHolder() {
instance = new BeanContainer();
}
} /**
* 是否已经加载过bean
*/
public boolean isLoad(){
return loaded;
} /**
* 获取容器中bean的个数
*/ public int getSize(){
return beanMap.size();
} /**
* 加载指定路径下的所有class文件
* 并将所有被配置标记(xml/注解)的class对象和new出一个实例对象放入容器
*/
public synchronized void loadBeans(String packageName){
//判断容器是否已经加载过
if (loaded){
log.warn("Container has loaded!");
return;
}
// 导出所有的class对象
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
//为空直接return
if (ValidationUtil.isEmpty(classSet)) {
log.warn("extract nothing from packageName" + packageName);
return;
} classSet.stream().forEach(clazz -> {
for (Class<? extends Annotation> annotationClazz : BEAN_ANNOTATION) {
if (clazz.isAnnotationPresent(annotationClazz)){
//将目标类本身作为键,目标类的实例作为值,放入到beanMap中
beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
}
} }); loaded = true;
} /**
* 添加一个class对象及其Bean实例
*
* @param clazz Class对象
* @param bean Bean实例
* @return 原有的Bean实例, 没有则返回null
*/
public Object addBean(Class<?> clazz, Object bean) {
return beanMap.put(clazz, bean);
} /**
* 移除一个IOC容器管理的对象
*
* @param clazz Class对象
* @return 删除的Bean实例, 没有则返回null
*/
public Object removeBean(Class<?> clazz) {
return beanMap.remove(clazz);
} /**
* 根据Class对象获取Bean实例
*
* @param clazz Class对象
* @return Bean实例
*/
public Object getBean(Class<?> clazz) {
return beanMap.get(clazz);
}
/**
* 获取容器管理的所有Class对象集合
*
* @return Class集合
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 获取所有Bean集合
*
* @return Bean集合
*/
public Set<Object> getBeans(){
return new HashSet<>( beanMap.values());
}
/**
* 根据注解筛选出Bean的Class集合
*
* @param annotation 注解
* @return Class集合
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.通过注解筛选被注解标记的class对象,并添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for(Class<?> clazz : keySet){
//类是否有相关的注解标记
if(clazz.isAnnotationPresent(annotation)){
classSet.add(clazz);
}
}
return classSet.size() > 0? classSet: null;
}
/**
* 通过接口或者父类获取实现类或者子类的Class集合,不包括其本身
*
* @param interfaceOrClass 接口Class或者父类Class
* @return Class集合
*/
public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
//1.获取beanMap的所有class对象
Set<Class<?>> keySet = getClasses();
if(ValidationUtil.isEmpty(keySet)){
log.warn("nothing in beanMap");
return null;
}
//2.判断keySet里的元素是否是传入的接口或者类的子类,如果是,就将其添加到classSet里
Set<Class<?>> classSet = new HashSet<>();
for(Class<?> clazz : keySet){
//判断keySet里的元素是否是传入的接口或者类的子类
if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
classSet.add(clazz);
}
}
return classSet.size() > 0? classSet: null;
} }

3.定义依赖注入器

核心功能:扫描被管理的bean对象,进行依赖注入。

package org.simplespring.inject;

import lombok.extern.slf4j.Slf4j;
import org.simplespring.core.BeanContainer;
import org.simplespring.inject.annotation.Autowired;
import org.simplespring.util.ClassUtil;
import org.simplespring.util.ValidationUtil;
import java.lang.reflect.Field;
import java.util.Set; /**
* @Classname DependencyInjector
* @Description 依赖注入器
* @Date 2020/8/6 14:38
* @Created by zhangtianci
*/
@Slf4j
public class DependencyInjector {
/**
* 拥有一个Bean容器
*/
private BeanContainer beanContainer;
public DependencyInjector(){
beanContainer = BeanContainer.getInstance();
} /**
* 执行依赖注入
*
* 1.遍历Bean容器中所有的Class对象
* 2.遍历Class对象的所有成员变量
* 3.找出被Autowired标记的成员变量
* 4.获取这些成员变量的类型
* 5.获取这些成员变量的类型在容器里对应的实例
* 6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
*/
public void doIoc(){
if(ValidationUtil.isEmpty(beanContainer.getClasses())){
log.warn("empty classset in BeanContainer");
return;
}
//1.遍历Bean容器中所有的Class对象
for(Class<?> clazz : beanContainer.getClasses()){
//2.遍历Class对象的所有成员变量
Field[] fields = clazz.getDeclaredFields();
if (ValidationUtil.isEmpty(fields)){
continue;
}
for(Field field : fields){
//3.找出被Autowired标记的成员变量
if(field.isAnnotationPresent(Autowired.class)){
Autowired autowired = field.getAnnotation(Autowired.class);
String autowiredValue = autowired.value();
//4.获取这些成员变量的类型
Class<?> fieldClass = field.getType();
//5.获取这些成员变量的类型在容器里对应的实例
Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
if(fieldValue == null){
throw new RuntimeException("unable to inject relevant type,target fieldClass is:" + fieldClass.getName() + " autowiredValue is : " + autowiredValue);
} else {
//6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
Object targetBean = beanContainer.getBean(clazz);
ClassUtil.setField(field, targetBean, fieldValue, true);
}
}
}
} }
/**
* 根据Class在beanContainer里获取其实例或者实现类
*/
private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
Object fieldValue = beanContainer.getBean(fieldClass);
if (fieldValue != null){
return fieldValue;
} else {
Class<?> implementedClass = getImplementedClass(fieldClass, autowiredValue);
if(implementedClass != null){
return beanContainer.getBean(implementedClass);
} else {
return null;
}
}
}
/**
* 获取接口的实现类
*/
private Class<?> getImplementedClass(Class<?> fieldClass, String autowiredValue) {
Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldClass);
if(!ValidationUtil.isEmpty(classSet)){
if(ValidationUtil.isEmpty(autowiredValue)){
if(classSet.size() == 1){
return classSet.iterator().next();
} else {
//如果多于两个实现类且用户未指定其中一个实现类,则抛出异常
throw new RuntimeException("multiple implemented classes for " + fieldClass.getName() + " please set @Autowired's value to pick one");
}
} else {
for(Class<?> clazz : classSet){//别名采用clazz.getSimpleName()简单实现
if(autowiredValue.equals(clazz.getSimpleName())){
return clazz;
}
}
}
}
return null;
} }

4.工具包

作为框架 类加载/反射/校验等功能。

package org.simplespring.util;

import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set; /**
* @Classname ClassUtil
* @Description
* @Date 2020/8/8 15:20
* @Created by zhangtianci
*/
@Slf4j
public class ClassUtil { public static final String FILE_PROTOCOL = "file"; /**
* 获取类加载器
* 因为项目部署在tomcat等一些web容器中
* 这些web容器加载部署在里面的apps 所以需要自定义classLoader去加载class文件
* 当我们写框架需要去通过类加载器去加载class文件时 就需要通过Thread.currentThread().getContextClassLoader()
* 拿到tomcat的自定义的classLoader去加载项目里面的class文件
* 详情 参考我写的classLoader加载相关文章
* @return
*/
public static ClassLoader getClassLoader(){
return Thread.currentThread().getContextClassLoader();
} /**
* 通过包名加载class
*
* 点进去Class.forName()方法进去看看
*
* @CallerSensitive
* public static Class<?> forName(String className)
* throws ClassNotFoundException {
* Class<?> caller = Reflection.getCallerClass();
* return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
* }
*
* 获取到调用这个方法的class对象 然后用这个class对象的classLoader去加载这个class文件
* 所以归根结底是 拿到tomcat的自定义的classLoader去加载的class文件
* 所以没问题
* @param className class全名=package + 类名
* @return
*/
public static Class<?> loadClass(String className){
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
log.error("loadClass failed!",e);
throw new RuntimeException(e);
} } /**
* 通过一个class对象实例化一个实例对象(通过默认构造器)
* @param clazz
* @param accessible
* @param <T>
* @return
*/
public static <T> T newInstance(Class<?> clazz, boolean accessible){
try {
//clazz.getDeclaredConstructor() 获取指定参数类表的构造器
//这里获取默认构造器
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(accessible);
return (T)constructor.newInstance();
} catch (Exception e) {
log.error("new instance failed!");
throw new RuntimeException(e);
}
} /**
* 设置类的属性值
*
* @param field 成员变量
* @param target 类实例
* @param value 成员变量的值
* @param accessible 是否允许设置私有属性
*/
public static void setField(Field field, Object target, Object value, boolean accessible){
field.setAccessible(accessible);
try {
field.set(target, value);
} catch (IllegalAccessException e) {
log.error("setField error", e);
throw new RuntimeException(e);
}
} /**
* 加载包路径下的所有的class文件
* 将class对象放进set集合中
*
* 1.获取classLoader加载器
* 2.递归加载路径下的所有class文件
* @param packageName
* @return
*/
public static Set<Class<?>> extractPackageClass(String packageName){ //1.获取classLoader加载器
ClassLoader classLoader = getClassLoader();
//2.获取资源文件的的url
URL url = classLoader.getResource(packageName.replace(".","/"));
if (url == null){
log.warn("unable to retrieve anything from package: " + packageName);
return null;
} //3.依据不同的资源类型,采用不同的方式获取资源的集合
Set<Class<?>> classSet = null;
//过滤出文件类型的资源
if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){
classSet = new HashSet<Class<?>>();
File packageDirectory = new File(url.getPath());
extractClassFile(classSet, packageDirectory, packageName);
}
//TODO 此处可以加入针对其他类型资源的处理 return null;
} /**
* 递归加载包路径下的class文件
* @param classSet
* @param packageDirectory
* @param packageName
*/
private static void extractClassFile(Set<Class<?>> classSet, File packageDirectory, String packageName) { if(!packageDirectory.isDirectory()){
return;
}
//如果是一个文件夹,则调用其listFiles方法获取文件夹下的文件或文件夹
File[] files = packageDirectory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory()){
return true;
} else{
//获取文件的绝对值路径
String absoluteFilePath = pathname.getAbsolutePath();
if(absoluteFilePath.endsWith(".class")){
//若是class文件,则直接加载
addToClassSet(absoluteFilePath);
}
}
return false;
} //根据class文件的绝对值路径,获取并生成class对象,并放入classSet中
private void addToClassSet(String absoluteFilePath) {
//1.从class文件的绝对值路径里提取出包含了package的类名
//如/Users/zhangtc/springframework/simple-spring/target/classes/com/zhangtianci/entity/dto/MainPageInfoDTO.class
//需要弄成com.zhangtianci.entity.dto.MainPageInfoDTO
absoluteFilePath = absoluteFilePath.replace(File.separator, ".");
String className = absoluteFilePath.substring(absoluteFilePath.indexOf(packageName));
className = className.substring(0, className.lastIndexOf("."));
//2.通过反射机制获取对应的Class对象并加入到classSet里
Class targetClass = loadClass(className);
classSet.add(targetClass);
}
}); if(files == null){
return;
}
Arrays.stream(files).forEach( file -> {
extractClassFile(classSet,file,packageName);
}); } }
package org.simplespring.util;

import java.util.Collection;
import java.util.Map; public class ValidationUtil {
/**
* String是否为null或""
*
* @param obj String
* @return 是否为空
*/
public static boolean isEmpty(String obj) {
return (obj == null || "".equals(obj));
} /**
* Array是否为null或者size为0
*
* @param obj Array
* @return 是否为空
*/
public static boolean isEmpty(Object[] obj) {
return obj == null || obj.length == 0;
}
/**
* Collection是否为null或size为0
*
* @param obj Collection
* @return 是否为空
*/
public static boolean isEmpty(Collection<?> obj){
return obj == null || obj.isEmpty();
}
/**
* Map是否为null或size为0
*
* @param obj Map
* @return 是否为空
*/
public static boolean isEmpty(Map<?, ?> obj) {
return obj == null || obj.isEmpty();
}
}

手写IOC实现过程的更多相关文章

  1. 闭关修炼180天--手写IOC和AOP(xml篇)

    闭关修炼180天--手写IOC和AOP(xml篇) 帝莘 首先先分享一波思维导图,涵盖了一些Spring的知识点,当然这里并不全面,后期我会持续更新知识点. 在手写实现IOC和AOP之前(也就是打造一 ...

  2. 手写AOP实现过程

    一.手写Aop前基础知识 1.aop是什么? 面向切面编程(AOP):是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行OOP开发时,都是基于对组件(比如类)进行开 ...

  3. 初学源码之——银行案例手写IOC和AOP

    手写实现lOC和AOP 上一部分我们理解了loC和AOP思想,我们先不考虑Spring是如何实现这两个思想的,此处准备了一个『银行转账」的案例,请分析该案例在代码层次有什么问题?分析之后使用我们已有知 ...

  4. 手写IOC实践

    一.IOC 1.什么是IOC? 控制反转(英语:Inversion of Control,缩写为IoC),是[面向对象编程]中的一种设计原则,可以用来减低计算机代码之间的[耦合度]其中最常见的方式叫做 ...

  5. 第三节:工厂+反射+配置文件(手写IOC)对缓存进行管理。

    一. 章前小节 在前面的两个章节,我们运用依赖倒置原则,分别对 System.Web.Caching.Cache和 System.Runtime.Cacheing两类缓存进行了封装,并形成了ICach ...

  6. 手写IOC容器

    IOC(控制翻转)是程序设计的一种思想,其本质就是上端对象不能直接依赖于下端对象,要是依赖的话就要通过抽象来依赖.这是什么意思呢?意思就是上端对象如BLL层中,需要调用下端对象的DAL层时不能直接调用 ...

  7. 手写IOC框架

    1.IOC框架的设计思路 ① 哪些类需要我们的容器进行管理 ②完成对象的别名和对应实例的映射装配 ③完成运行期对象所需要的依赖对象的依赖

  8. 手写SpringMVC实现过程

    1. Spring Boot,Spring MVC的底层实现都是Servlet的调用. 2. Servlet的生命周期里面首先是类的初始化,然后是类的方法的调用,再次是类的销毁. 3. 创建一个spr ...

  9. 我自横刀向天笑,手写Spring IOC容器,快来Look Look!

    目录 IOC分析 IOC是什么 IOC能够带来什么好处 IOC容器是做什么工作的 IOC容器是否是工厂模式的实例 IOC设计实现 设计IOC需要什么 定义接口 一:Bean工厂接口 二:Bean定义的 ...

随机推荐

  1. Java实现 LeetCode第30场双周赛 (题号5177,5445,5446,5447)

    这套题不算难,但是因为是昨天晚上太晚了,好久没有大晚上写过代码了,有点不适应,今天上午一看还是挺简单的 5177. 转变日期格式   给你一个字符串 date ,它的格式为 Day Month Yea ...

  2. 从零开始学Electron笔记(五)

    在之前的文章我们介绍了一下Electron的右键菜单的制作,接下来我们继续说一下Electron如何通过链接打开浏览器和嵌入网页. 现在有这样一个需求,我们要在我们的软件中加一个链接,然后点击该链接打 ...

  3. Idea 自定义快捷代码输入 如syso => System.out.println()

    前言 之前一直用的Eclipse System.out.println()的快捷代码输入 是 syso,但是在Idea 不好使用了,后来搜索了一番才知道,在Idea中的快捷输入是 sout,这里我就想 ...

  4. 微信小程序 + wepy快速开发

    wepy官网:https://tencent.github.io/wepy/document.html,想要了解全面最好去官网,以下只是指出项目里常用地方. 1.页面跳转 (1)//有返回跳转wepy ...

  5. 循序渐进VUE+Element 前端应用开发(17)--- 菜单资源管理

    在权限管理系统中,菜单也属于权限控制的一个资源,应该直接应用于角色,和权限功能点一样,属于角色控制的一环.不同角色用户,登录系统后,出现的系统菜单是不同的.在VUE+Element 前端中,我们菜单结 ...

  6. XML解析---利用XStream解析xml数据及反构造Java对象

    XStream 是一个轻量级的.简单易用的开放源代码 Java库,用于将 Java 对象序列化为 XML 或者再转换回来.而且XStream还能将java对象转成其它格式,比如JSon. 需要用到的包 ...

  7. Burp Suite Repeater Module - 中继模块

    目的:节省网页应用分析时间 目标对象:http://10.0.0.15/getboo/login.php 通过调整Request的参数,不断尝试,通过Response查看状态.从而节省在浏览器中操作的 ...

  8. 想用@Autowired注入static静态成员?官方不推荐你却还偏要这么做

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  9. LGV定理

    LGV定理用于解决路径不相交问题. 定理 有 \(n\) 个起点 \(1, 2, 3, ..., n\),它们 分别对应 要到 \(n\) 个终点 \(A, B, C, ..., X\),并且要求路径 ...

  10. hdu6755 Mow

    半平面交+数组模拟双端队列 人生第一次代码过两百行啊...加油加油 #include<iostream> #include<algorithm> #include<cma ...