1 前言

  1. Spring是一个轻量级开源框架,它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
  2. Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
  3. Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
  4. Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。

2 Spring架构组成

Spring架构由诸多模块组成,可分类为

  • 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP。
  • 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
  • 数据访问:事务,DAO支持,JDBC,ORM,封送XML。
  • Spring MVC和 Spring WebFlux Web框架。
  • 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
  • 语言:Kotlin,Groovy,动态语言。
  • List item

Spring架构组成如下图

3 Spring环境搭建

3.1 pom.xml中引入Spring常用依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.qf</groupId>
<artifactId>hello-spring</artifactId>
<version>1.0-SNAPSHOT</version> <dependencies>
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
</project>

3.2 创建Spring配置文件

命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>

4 Spring工厂编码(入门程序)

4.1 定义目标Bean类型

public class MyClass{
public void show(){
System.out.println("HelloWorld");
}
}

4.2spring-context.xml中的< beans >内部配置bean

在spring-context.xml中配置MyClass的bean后,当项目启动时,spring容器会自动创建MyClass实例,这个实例名字叫mc

<!-- 配置实例(id:“唯一标识”  class="需要被创建的目标对象全限定名") -->
<bean id="mc" class="com.qf.spring.part1.factory.MyClass" />

测试代码

public class TestFactory{
/**
* 程序中的对象都交由Spring的ApplicationContext工厂进行创建。
*/
public static void main(String[] args){
//1\. 读取配置文件中所需创建的bean对象,并获得工厂对象
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
//2\. 通过id获取bean对象
MyClass mc = (MyClass) ctx.getBean("mc");
//3\. 使用对象
mc.show();
}
}

5 IoC(Inversion of Control )控制反转

控制反转是Spring框架的核心,所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的, 这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。这样就由之前的自己创建依赖对象,变为由spring容器创建。(变主动为被动,即反转)。控制反转解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健。

5.1 项目中强耦合问题

public class UserDAOImpl implements UserDAO{....}

public class UserServiceImpl implements UserService {
// 通过传统的new方式强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不稳健!!
private UserDAO userDAO= new UserDAOImpl();
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}

5.2 解决方案

// 不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
// !!!不再耦合任何DAO实现!!!,消除不稳健因素!!
private UserDAO userDAO;
// 为userDAO定义set/get,允许userDAO属性接收spring赋值
//Getters And Setters
@Override
public User queryUser() {
return userDAO.queryUser();
}
....
}

在spring配置文件中配置UserDAO和UserService对应的bean

<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean>
<!-- UserServiceImpl组件 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl">
<!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean -->
<property name="userDAO" ref="userDAO"/>
</bean>

6 DI(Dependency Injection)依赖注入

6.1 概念

在Spring创建对象的同时,为其属性赋值,称之为依赖注入,注入方式主要有以下2种

  • 构造函数注入
  • Setter方法注入

6.2 Setter方法注入

创建对象时,Spring工厂会通过Setter方法为对象的属性赋值。

6.2.1 定义目标Bean类型

public class User {
private Integer id;
private String password;
private String sex;
private Integer age;
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
//Getters And Setters
}

6.2.2 基本类型 + 字符串类型 + 日期类型

<bean id="u1" class="com.qf.spring.part1.injection.User">
<!--base field-->
<property name="id" value="1001" />
<property name="password" value="123456" />
<property name="sex" value="male" />
<property name="age" value="20" />
<property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>

6.2.3 容器类型(list,set,map,Properties)

<bean id="u1" class="com.qf.spring.part1.injection.User">
<!--Array-->
<property name="hobbys">
<array>
<value>Run</value>
<value>Swim</value>
<value>Climb</value>
</array>
</property> <!--Set-->
<property name="phones">
<set>
<value>13777777777</value>
<value>13888888888</value>
<value>13999999999</value>
</set>
</property> <!--List-->
<property name="names">
<list>
<value>tom</value>
<value>jack</value>
<value>marry</value>
</list>
</property> <!--Map-->
<property name="countries">
<map>
<entry key="CN" value="China" />
<entry key="US" value="America" />
<entry key="KR" value="Korea" />
</map>
</property> <!--Properties-->
<property name="files">
<props>
<prop key="first">One</prop>
<prop key="second">Two</prop>
<prop key="third">Three</prop>
</props>
</property>
</bean>

6.2.4 自定义类型

<!--次要bean,被作为属性-->
<bean id="addr" class="com.qf.spring.part1.injection.Address">
<property name="position" value="北京市海淀区" />
<property name="zipCode" value="100001" />
</bean> <!--主要bean,操作的主体-->
<bean id="u2" class="com.qf.spring.part1.injection.User">
<property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>

6.3 构造注入

创建对象时,Spring工厂会通过构造方法为对象的属性赋值。

6.3.1 定义目标Bean类型

public class Student {
private Integer id;
private String name;
private String sex;
private Integer age; //Constructors
public Student(Integer id , String name , String sex , Integer age){
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
}

6.3.2 注入

 <!--构造注入-->
<bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student">
<constructor-arg name="id" value="1234" /> <!-- 除标签名称有变化,其他均和Set注入一致 -->
<constructor-arg name="name" value="tom" />
<constructor-arg name="age" value="20" />
<constructor-arg name="sex" value="male" />
</bean>

7 Spring工厂特性

7.1 饿汉式创建优势

工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式),提高程序运行效率,避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)

7.2 生命周期方法

  • 自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
  • 自定义销毁方法:添加“destroy-method”属性,Spring则会在销毁对象之前,调用此方法。
  • 销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
  • 分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。

7.3 生命周期注解

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; @PostConstruct //初始化
public void init(){
System.out.println("init method executed");
} @PreDestroy //销毁
public void destroy(){
System.out.println("destroy method executed");
}

7.4 生命周期阶段

单例bean:singleton

随工厂启动创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》随工厂关闭销毁

多例bean:prototype

被使用时创建 ==》 构造方法 ==》 set方法(注入值) ==》 init(初始化) ==》 构建完成 ==》JVM垃圾回收销毁

8 代理设计模式

8.1 概念

将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。

8.2 静态代理设计模式

通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。

  • 代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。
  • 静态代理的问题
    • 代理类数量过多,不利于项目的管理。
    • 多个代理类的辅助功能代码冗余,修改时,维护性差。

8.3 动态代理设计模式

8.3.1 JDK动态代理实现(基于接口)

//目标
final OrderService os = new OrderServiceImpl();
//额外功能
InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额外功能代码)
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("start...");
method.invoke(os, args);
System.out.println("end...");
return null;
}
};
//2.创建动态代理类
Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);

8.3.2 CGlib动态代理实现(基于继承)

final OrderService os = new OrderServiceImpl();
Enhancer cnh = new Enhancer();//1.创建字节码曾强对象
enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口)
enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码)
@Override
public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{
System.out.println("start...");
Object ret = method.invoke(os,args);
System.out.println("end...");
return ret;
}
});
OrderService proxy = (OrderService)enh.create();//4.创建动态代理类
proxy,createOrder();

9 面向切面编程

9.1 概念

AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

通俗的概念来讲,所谓的面向切面编程就是针对被代理对象的方法在某个特定的执行时机(方法调用之前、方法调用之后、方法抛出异常),做出一些额外的横向的处理。好处在于:1. 可以在不修改原有代码基础上横向扩展我们的内容;2. 将一些方法中的通用逻辑进行统一化的处理。

OOP(面向对象编程)和AOP(面向切面编程)的区别:oop是对类纵向的扩展;aop是横向的扩展。

9.2 AOP开发术语

  • 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
  • 切入点(Pointcut):被切入连接点。
  • 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
  • 目标对象(Target):代理的目标对象
  • 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
  • 代理(Proxy):被AOP织入通知后,产生的结果类。
  • 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。

9.3 作用

Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。

9.4 环境搭建

引入AOP相关依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>

spring-context.xml引入AOP命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>

9.5 开发流程

定义原始类

package com.qf.aaron.aop.basic;

public interface UserService {
public void save();
}
package com.qf.aaron.aop.basic;

public class UserServiceImpl implements UserService {
public void save() {
System.out.println("save method executed...");
}
}

定义通知类(添加额外功能

package com.qf.aaron.aop.basic;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method; public class MyAdvice implements MethodBeforeAdvice { //实现前置通知接口
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before advice executed...");
}
}

定义bean标签

<!--原始对象-->
<bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /> <!--辅助对象-->
<bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />

定义切入点(PointCut)

形成切面(Aspect)

<aop:config>
<!--切点-->
<aop:pointcut id="myPointCut" expression="execution(* save())" />
</aop:config> ```java
<aop:config>
<!--组装切面 -->
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" />
</aop:config>

9.6 通知类

可定义的通知类有6种,可以按需求选择通知类。

前置通知:MethodBeforeAdvice

后置通知:AfterAdvice

后置通知:AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值

异常通知:ThrowsAdvice

环绕通知:MethodInterceptor

9.7 JDK动态代理和CGLIB动态代理的选择

  • spring底层,包含了jdk代理和cglib代理两种动态代理生成机制
  • 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理

但是spring中默认开启JDK动态代理,当需要使用CGLIB动态代理时,需要在spring配置文件中配置。

<!--  使用cglib的方式实现aop -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

10 基于aspectJ的AOP实现

编写aspectJ通知代码:

@Aspect
public class AspectJAdvisor { // 环绕通知
@Around("execution(* org.example.service.impl.*.*(..))")
public Object timer(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("前置通知");
Object o = pjp.proceed();
System.out.println("【后置通知】");
return o;
} // 后置通知
@After("execution(* org.example.service.impl.*.*(..))")
public void after(JoinPoint jp) throws Throwable{
System.out.println("====After method invokded====");
} // 前置通知
@Before("execution(* org.example.service.impl.*.*(..))")
public void before(JoinPoint jp){
System.out.println("====before method invoked====");
}
// 正常返回的通知
@AfterReturning("execution(* org.example.service.impl.*.*(..))")
public void afterReturning(JoinPoint jp){
System.out.println("====after value return====");
}
// 抛出异常后的通知,方法的异常必须与代理类抛出的异常一致,throwing的值要与异常的形参名保持一致
@AfterThrowing(value = "execution(* org.example.service.impl.*.*(..))", throwing="npe")
public void afterThrowException(JoinPoint jp, NullPointerException npe){
System.out.println("====after exception throwing====");
}
}

配置

<bean id="userService" class="org.example.service.impl.UserServiceImpl"></bean>

<bean id="throwsAdvisor" class="org.example.advisor.AspectJAdvisor"></bean>

<!--- aspectJ是使用cglib来实现动态代理的 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

11 基于注解开发

11.1 声明bean

用于替换自建类型组件的 <bean…>标签;可以更快速的声明bean

  • @Service 业务类专用

    @Repository dao实现类专用

    @Controller web层专用
  • @Component 通用
  • @Scope 用户控制bean的创建模式
// @Service说明 此类是一个业务类,需要将此类纳入工厂  等价替换掉 <bean class="xxx.UserServiceImpl">
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //声明bean,且id="userServiceImpl"
@Scope("singleton") //声明创建模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements UserService {
...
}

11.2 注入(DI)

用于完成bean中属性值的注入

  • @Autowired 基于类型自动注入
  • @Resource 基于名称自动注入
  • @Qualifier(“userDAO”) 限定要自动注入的bean的id,一般和@Autowired联用
  • @Value 注入简单类型数据 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService { @Autowired //注入类型为UserDAO的bean
@Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,可以用此注解从中挑选一个
private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService { @Resource("userDAO3") //注入id=“userDAO3”的bean
private UserDAO userDAO;
/*
@Resource //注入id=“userDAO”的bean
private UserDAO userDAO;
*/
}
public class XX{
@Value("100") //注入数字
private Integer id;
@Value("shine") //注入String
private String name;
}

11.3 事务控制

用于控制事务切入

  • @Transactional
  • 工厂配置中的 <tx:advice… 和 <aop:config… 可以省略 !!
//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {
...
//该方法自己的事务控制,仅对此方法有效
@Transactional(propagation=Propagation.SUPPORTS)
public List<User> queryAll() {
return userDao.queryAll();
}
public void save(User user){
userDao.save(user);
}
}

11.4 注解所需配置

<!-- 告知spring,哪些包中 有被注解的类、方法、属性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan> <!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager -->
<tx:annotation-driven transaction-manager="txManager"/>

11.5 AOP开发

11.5.1 注解使用

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; @Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
// 定义切入点
@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")
public void pc(){} @Before("pc()") // 前置通知
public void mybefore(JoinPoint a) {
System.out.println("target:"+a.getTarget());
System.out.println("args:"+a.getArgs());
System.out.println("method's name:"+a.getSignature().getName());
System.out.println("before~~~~");
} @AfterReturning(value="pc()",returning="ret") // 后置通知
public void myAfterReturning(JoinPoint a,Object ret){
System.out.println("after~~~~:"+ret);
} @Around("pc()") // 环绕通知
public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
System.out.println("interceptor1~~~~");
Object ret = p.proceed();
System.out.println("interceptor2~~~~");
return ret;
} @AfterThrowing(value="pc()",throwing="ex") // 异常通知
public void myThrows(JoinPoint jp,Exception ex){
System.out.println("throws");
System.out.println("===="+ex.getMessage());
}
}

11.5.2 配置

<!-- 添加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

12 Spring单元测试

12.1 导入依赖

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

12.2 测试编码

可以免去工厂的创建过程;

可以直接将要测试的组件注入到测试类。

@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置
public class SpringTest{//当前测试类也会被纳入工厂中,所以其中属性可以注入 @Autowired // 注入要测试的组件
@Qualifier("userDAO")
private UserDAO userDAO; @Test
public void test(){
// 测试使用userDAO
userDAO.queryUser();
....
}
}

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

面试阿里,美团,京东都会被问到的Spring ,从基础到源码帮你全搞定的更多相关文章

  1. 面试阿里,腾讯,字节跳动90%都会被问到的Spring中的循环依赖

    前言 Spring中的循环依赖一直是Spring中一个很重要的话题,一方面是因为源码中为了解决循环依赖做了很多处理,另外一方面是因为面试的时候,如果问到Spring中比较高阶的问题,那么循环依赖必定逃 ...

  2. 👨‍💻Mybatis源码我搞透了,面试来问吧!写了134个源码类,1.03万行代码!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言:手撸一万行! 完结撒花:4个月.20章.134个类.1.03万行代码! 22年3月初 ...

  3. 面试官问我:看过sharding-jdbc的源码吗?我吧啦吧啦说了一通!!

    写在前面 在产品初期快速迭代的过程中,往往为了快速上线而占据市场,在后端开发的过程中往往不会过多的考虑分布式和微服务,往往会将后端服务做成一个单体应用,而数据库也是一样,最初会把所有的业务数据都放到一 ...

  4. iOS漂亮的Toolbar动画、仿美团主页、简易笔记本、流失布局、标签分组等源码

    iOS精选源码 JPLiquidLayout 简单易用的流式布局 labelGroupAndStreamSwift---标签分组,单选,多选 iOS采用UITableView和UIScrollView ...

  5. 面试 10:玩转 Java 选择和插入排序,附冒泡最终源码

    昨天给大家讲解了 Java 玩转冒泡排序,大家一定觉得并没有什么难度吧,不知道大佬们玩转了吗?不知道大家有没有多加思考,实际上在我们最后的一种思路上,还可以再继续改进. 我们先看看昨天最终版本的代码. ...

  6. 面试腾讯,字节跳动首先要掌握的Java多线程,一次帮你全掌握!

    一.程序,进程,线程联系和区别 其实程序是一段静态的代码,它是应用程序执行的脚本.进程就是程序动态的执行过程,它具有动态性,并发性,独立性.线程是进程调度和执行的单位. 进程:每个进程都有独立的代码和 ...

  7. 【笔记0-开篇】面试官系统精讲Java源码及大厂真题

    背景 开始阅读 Java 源码的契机,还是在第一年换工作的时候,被大厂的技术面虐的体无完肤,后来总结大厂的面试套路,发现很喜欢问 Java 底层实现,即 Java 源码,于是我花了半年时间,啃下了 J ...

  8. 面试阿里被“吊打”,一问Spring三不知,半年后二战终拿下offer

    Spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发. 近两年来,许多大厂在面 ...

  9. 新鲜出炉!春招-面试-阿里钉钉、头条广告,美团面经分享,看我如何拿下offer!

    之前给大家分享了一个朋友在字节面试的面试经历和拿到offer的过程,过程也算是比较精彩了,感兴趣的朋友可以去翻翻之前的那篇文章.话不多说重点来啦,一直有人发私信问我有没有其他大厂的面经分享啊,我也是联 ...

随机推荐

  1. xib使用

    xib和storyboard都可以建立应用程序的视图.他们的主要区别在于,xib用于创建应用程序的局部视图,storyboard用于创建应用程序的整体视图. xib是storyboard的前身. xi ...

  2. eclipse 开发常见问题集锦

    问题1: eclipse导入外部项目,中文显示乱码(如下图) 方案:项目名-->右键属性-->如下图: 问题2: jsp/html页面eclipse双击打开,代码在工作区不显示(如下图:) ...

  3. java后端选型20200729

    参考地址:https://gitee.com/shuzheng/zheng 后端技术: 技术 名称 官网 Spring Framework 容器 http://projects.spring.io/s ...

  4. 获取url中查询字符串参数

    // 获取url中查询字符串参数 例如http://www.test.com?a=1&b=2 function RequestParamete() { var url = window.loc ...

  5. MONGODB03 - 分组计数_分组去重计数(基于 spring-data-mongodb)

    前因 项目中有查询MongoDB单表统计相关功能,涉及到MongoDB数据聚合相关操作,其中在多字段分组去重计数相关操作API上资料较少,spring-data-mongodb相关的API介绍也不够直 ...

  6. re模块,判断某行/某字符是否存在

    import re ##判断行是否存在def get_need_line(): ## 获取有用信息行 with open('task.log',mode="r") as f: fo ...

  7. linux上性能调优常用命令及简介

    1.综合命令:nmon.top:topas(aix) d :磁盘相关 c:cpu相关 m:内存相关 2.磁盘 2.1 测试顺序写性能dd if=/dev/zero of=/cdr/test.data ...

  8. python实现密码破解

    排列组合(破解密码) 关注公众号"轻松学编程"了解更多. 1.排列 itertools.permutations(iterable,n) 参数一:要排列的序列, 参数二:要选取的个 ...

  9. Nginx是什么?有什么用?

    一.Nginx是什么 Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服 ...

  10. 如何做可靠的分布式锁,Redlock真的可行么

    本文是对 Martin Kleppmann 的文章 How to do distributed locking 部分内容的翻译和总结,上次写 Redlock 的原因就是看到了 Martin 的这篇文章 ...