本文曾经收录到Github仓库,该仓库蕴含计算机根底、Java根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等外围知识点,欢送star~

Github地址

Spring的长处

  • 通过管制反转和依赖注入实现松耦合
  • 反对面向切面的编程,并且把利用业务逻辑和零碎服务离开。
  • 通过切面和模板缩小样板式代码。
  • 申明式事务的反对。能够从枯燥繁冗的事务管理代码中解脱进去,通过申明式形式灵便地进行事务的治理,进步开发效率和品质。
  • 不便集成各种优良框架。外部提供了对各种优良框架的间接反对(如:Hessian、Quartz、MyBatis等)。
  • 不便程序的测试。Spring反对Junit4,增加注解便能够测试Spring程序。

Spring 用到了哪些设计模式?

1、简略工厂模式BeanFactory就是简略工厂模式的体现,依据传入一个惟一标识来取得 Bean 对象。

@Overridepublic Object getBean(String name) throws BeansException {    assertBeanFactoryActive();    return getBeanFactory().getBean(name);}

2、工厂办法模式FactoryBean就是典型的工厂办法模式。spring在应用getBean()调用取得该bean时,会主动调用该bean的getObject()办法。每个 Bean 都会对应一个 FactoryBean,如 SqlSessionFactory 对应 SqlSessionFactoryBean

3、单例模式:一个类仅有一个实例,提供一个拜访它的全局拜访点。Spring 创立 Bean 实例默认是单例的。

4、适配器模式:SpringMVC中的适配器HandlerAdatper。因为利用会有多个Controller实现,如果须要间接调用Controller办法,那么须要先判断是由哪一个Controller解决申请,而后调用相应的办法。当减少新的 Controller,须要批改原来的逻辑,违反了开闭准则(对批改敞开,对扩大凋谢)。

为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 HandlerAdapter 实现类,当申请过去,SpringMVC会调用getHandler()获取相应的Controller,而后获取该Controller对应的 HandlerAdapter,最初调用HandlerAdapterhandle()办法解决申请,实际上调用的是Controller的handleRequest()。每次增加新的 Controller 时,只须要减少一个适配器类就能够,无需批改原有的逻辑。

罕用的处理器适配器:SimpleControllerHandlerAdapterHttpRequestHandlerAdapterAnnotationMethodHandlerAdapter

// Determine handler for the current request.mappedHandler = getHandler(processedRequest);HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());public class HttpRequestHandlerAdapter implements HandlerAdapter {    @Override    public boolean supports(Object handler) {//handler是被适配的对象,这里应用的是对象的适配器模式        return (handler instanceof HttpRequestHandler);    }    @Override    @Nullable    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)        throws Exception {        ((HttpRequestHandler) handler).handleRequest(request, response);        return null;    }}

5、代理模式:spring 的 aop 应用了动静代理,有两种形式JdkDynamicAopProxyCglib2AopProxy

6、观察者模式:spring 中 observer 模式罕用的中央是 listener 的实现,如ApplicationListener

7、模板模式: Spring 中 jdbcTemplatehibernateTemplate 等,就应用到了模板模式。

什么是AOP?

面向切面编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行拆散,能够缩小零碎的反复代码和升高模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。

AOP有哪些实现形式?

AOP有两种实现形式:动态代理和动静代理。

动态代理

动态代理:代理类在编译阶段生成,在编译阶段将告诉织入Java字节码中,也称编译时加强。AspectJ应用的是动态代理。

毛病:代理对象须要与指标对象实现一样的接口,并且实现接口的办法,会有冗余代码。同时,一旦接口减少办法,指标对象与代理对象都要保护。

动静代理

动静代理:代理类在程序运行时创立,AOP框架不会去批改字节码,而是在内存中长期生成一个代理对象,在运行期间对业务办法进行加强,不会生成新类。

Spring AOP的实现原理

SpringAOP实现原理其实很简略,就是通过动静代理实现的。如果咱们为Spring的某个bean配置了切面,那么Spring在创立这个bean的时候,实际上创立的是这个bean的一个代理对象,咱们后续对bean中办法的调用,实际上调用的是代理类重写的代理办法。而SpringAOP应用了两种动静代理,别离是JDK的动静代理,以及CGLib的动静代理

JDK动静代理和CGLIB动静代理的区别?

Spring AOP中的动静代理次要有两种形式:JDK动静代理和CGLIB动静代理。

JDK动静代理

如果指标类实现了接口,Spring AOP会抉择应用JDK动静代理指标类。代理类依据指标类实现的接口动静生成,不须要本人编写,生成的动静代理类和指标类都实现雷同的接口。JDK动静代理的外围是InvocationHandler接口和Proxy类。

毛病:指标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动静代理。

CGLIB动静代理

通过继承实现。如果指标类没有实现接口,那么Spring AOP会抉择应用CGLIB来动静代理指标类。CGLIB(Code Generation Library)能够在运行时动静生成类的字节码,动态创建指标类的子类对象,在子类对象中加强指标类。

CGLIB是通过继承的形式做的动静代理,因而如果某个类被标记为final,那么它是无奈应用CGLIB做动静代理的。

长处:指标类不须要实现特定的接口,更加灵便。

什么时候采纳哪种动静代理?

  1. 如果指标对象实现了接口,默认状况下会采纳JDK的动静代理实现AOP
  2. 如果指标对象实现了接口,能够强制应用CGLIB实现AOP
  3. 如果指标对象没有实现了接口,必须采纳CGLIB库

两者的区别

  1. jdk动静代理应用jdk中的类Proxy来创立代理对象,它应用反射技术来实现,不须要导入其余依赖。cglib须要引入相干依赖:asm.jar,它应用字节码加强技术来实现。
  2. 当指标类实现了接口的时候Spring Aop默认应用jdk动静代理形式来加强办法,没有实现接口的时候应用cglib动静代理形式加强办法。

Spring AOP相干术语

(1)切面(Aspect):切面是告诉和切点的联合。告诉和切点独特定义了切面的全部内容。

(2)连接点(Join point):指办法,在Spring AOP中,一个连接点总是代表一个办法的执行。连接点是在利用执行过程中可能插入切面的一个点。这个点能够是调用办法时、抛出异样时、甚至批改一个字段时。切面代码能够利用这些点插入到利用的失常流程之中,并增加新的行为。

(3)告诉(Advice):在AOP术语中,切面的工作被称为告诉。

(4)切入点(Pointcut):切点的定义会匹配告诉所要织入的一个或多个连接点。咱们通常应用明确的类和办法名称,或是利用正则表达式定义所匹配的类和办法名称来指定这些切点。

(5)引入(Introduction):引入容许咱们向现有类增加新办法或属性。

(6)指标对象(Target Object): 被一个或者多个切面(aspect)所告诉(advise)的对象。它通常是一个代理对象。

(7)织入(Weaving):织入是把切面利用到指标对象并创立新的代理对象的过程。在指标对象的生命周期里有以下工夫点能够进行织入:

  • 编译期:切面在指标类编译时被织入。AspectJ的织入编译器是以这种形式织入切面的。
  • 类加载期:切面在指标类加载到JVM时被织入。须要非凡的类加载器,它能够在指标类被引入利用之前加强该指标类的字节码。AspectJ5的加载时织入就反对以这种形式织入切面。
  • 运行期:切面在利用运行的某个时刻被织入。个别状况下,在织入切面时,AOP容器会为指标对象动静地创立一个代理对象。SpringAOP就是以这种形式织入切面。

Spring告诉有哪些类型?

在AOP术语中,切面的工作被称为告诉。告诉实际上是程序运行时要通过Spring AOP框架来触发的代码段。

Spring切面能够利用5种类型的告诉:

  1. 前置告诉(Before):在指标办法被调用之前调用告诉性能;
  2. 后置告诉(After):在指标办法实现之后调用告诉,此时不会关怀办法的输入是什么;
  3. 返回告诉(After-returning ):在指标办法胜利执行之后调用告诉;
  4. 异样告诉(After-throwing):在指标办法抛出异样后调用告诉;
  5. 盘绕告诉(Around):告诉包裹了被告诉的办法,在被告诉的办法调用之前和调用之后执行自定义的逻辑。

什么是依赖注入?

在Spring创建对象的过程中,把对象依赖的属性注入到对象中。依赖注入次要有两种形式:结构器注入和属性注入。什么是IOC?

IOC:管制反转,由Spring容器治理bean的整个生命周期。通过反射实现对其余对象的管制,包含初始化、创立、销毁等,解放手动创建对象的过程,同时升高类之间的耦合度。

IOC的益处?

ioc的思维最外围的中央在于,资源不禁应用资源者治理,而由不应用资源的第三方治理,这能够带来很多益处。第一,资源集中管理,实现资源的可配置和易治理。第二,升高了应用资源单方的依赖水平,也就是咱们说的耦合度。

也就是说,甲方要达成某种目标不须要间接依赖乙方,它只须要达到的目标通知第三方机构就能够了,比方甲方须要一双袜子,而乙方它卖一双袜子,它要把袜子卖出去,并不需要本人去间接找到一个卖家来实现袜子的卖出。它也只须要找第三方,通知他人我要卖一双袜子。这下好了,甲乙双方进行交易流动,都不须要本人间接去找卖家,相当于程序外部凋谢接口,卖家由第三方作为参数传入。甲乙相互不依赖,而且只有在进行交易流动的时候,甲才和乙产生分割。反之亦然。这样做什么益处么呢,甲乙能够在对方不实在存在的状况下独立存在,而且保障不交易时候无分割,想交易的时候能够很容易的产生分割。甲乙交易流动不须要单方见面,防止了单方的互不信赖造成交易失败的问题。因为交易由第三方来负责分割,而且甲乙都认为第三方牢靠。那么交易就能很牢靠很灵便的产生和进行了。

这就是ioc的核心思想。生存中这种例子亘古未有,支付宝在整个淘宝体系里就是宏大的ioc容器,交易单方之外的第三方,提供可靠性可依赖可灵便变更交易方的资源管理核心。另外人事代理也是,雇佣机构和集体之外的第三方。

参考链接:https://www.zhihu.com/question/23277575/answer/24259844

IOC容器初始化过程?

  1. 从XML中读取配置文件。
  2. 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
  3. 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
  4. BeanFactory 依据 BeanDefinition 的定义信息创立实例化和初始化 bean。

单例bean的初始化以及依赖注入个别都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在利用第一次调用getBean()时进行初始化和依赖注入。

// AbstractApplicationContext// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);

多例bean 在容器启动时不实例化,即便设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。

loadBeanDefinitions采纳了模板模式,具体加载 BeanDefinition 的逻辑由各个子类实现。

Bean的生命周期

1.调用bean的构造方法创立Bean

2.通过反射调用setter办法进行属性的依赖注入

3.如果Bean实现了BeanNameAware接口,Spring将调用setBeanName(),设置 Bean的name(xml文件中bean标签的id)

4.如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()把bean factory设置给Bean

5.如果存在BeanPostProcessor,Spring将调用它们的postProcessBeforeInitialization(预初始化)办法,在Bean初始化前对其进行解决

6.如果Bean实现了InitializingBean接口,Spring将调用它的afterPropertiesSet办法,而后调用xml定义的 init-method 办法,两个办法作用相似,都是在初始化 bean 的时候执行

7.如果存在BeanPostProcessor,Spring将调用它们的postProcessAfterInitialization(后初始化)办法,在Bean初始化后对其进行解决

8.Bean初始化实现,供给用应用,这里分两种状况:

8.1 如果Bean为单例的话,那么容器会返回Bean给用户,并存入缓存池。如果Bean实现了DisposableBean接口,Spring将调用它的destory办法,而后调用在xml中定义的 destory-method办法,这两个办法作用相似,都是在Bean实例销毁前执行。

8.2 如果Bean是多例的话,容器将Bean返回给用户,剩下的生命周期由用户管制。

public interface BeanPostProcessor {    @Nullable    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        return bean;    }    @Nullable    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        return bean;    }}public interface InitializingBean {    void afterPropertiesSet() throws Exception;}

BeanFactory和FactoryBean的区别?

BeanFactory:治理Bean的容器,Spring中生成的Bean都是由这个接口的实现来治理的。

FactoryBean:通常是用来创立比较复杂的bean,个别的bean 间接用xml配置即可,但如果一个bean的创立过程中波及到很多其余的bean 和简单的逻辑,间接用xml配置比拟麻烦,这时能够思考用FactoryBean,能够暗藏实例化简单Bean的细节。

当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()办法返回的不是FactoryBean自身,而是调用FactoryBean#getObject()办法所返回的对象,相当于FactoryBean#getObject()代理了getBean()办法。如果想得到FactoryBean必须应用 '&' + beanName 的形式获取。

Mybatis 提供了 SqlSessionFactoryBean,能够简化 SqlSessionFactory的配置:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {  @Override  public void afterPropertiesSet() throws Exception {    notNull(dataSource, "Property 'dataSource' is required");    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),              "Property 'configuration' and 'configLocation' can not specified with together");    this.sqlSessionFactory = buildSqlSessionFactory();  }  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {    //简单逻辑  }      @Override  public SqlSessionFactory getObject() throws Exception {    if (this.sqlSessionFactory == null) {      afterPropertiesSet();    }    return this.sqlSessionFactory;  }}

在 xml 配置 SqlSessionFactoryBean:

<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    <property name="dataSource" ref="trade" />    <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />    <property name="configLocation" value="classpath:mybatis-config.xml" />    <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" /></bean>

Spring 将会在利用启动时创立 SqlSessionFactory,并应用 sqlSessionFactory 这个名字存储起来。

BeanFactory和ApplicationContext有什么区别?

BeanFactory和ApplicationContext是Spring的两大外围接口,都能够当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。

两者区别如下:

1、性能上的区别。BeanFactory是Spring外面最底层的接口,蕴含了各种Bean的定义,读取bean配置文档,治理bean的加载、实例化,管制bean的生命周期,保护bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具备的性能外,还提供了更残缺的框架性能,如继承MessageSource、反对国际化、对立的资源文件拜访形式、同时加载多个配置文件等性能。

2、加载形式的区别。BeanFactroy采纳的是提早加载模式来注入Bean的,即只有在应用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,咱们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次应用调用getBean办法才会抛出异样。

而ApplicationContext是在容器启动时,一次性创立了所有的Bean。这样,在容器启动时,咱们就能够发现Spring中存在的配置谬误,这样有利于查看所依赖属性是否注入。 ApplicationContext启动后预载入所有的单例Bean,那么在须要的时候,不须要期待创立bean,因为它们曾经创立好了。

绝对于根本的BeanFactory,ApplicationContext 惟一的有余是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

3、创立形式的区别。BeanFactory通常以编程的形式被创立,ApplicationContext还能以申明的形式创立,如应用ContextLoader。

4、注册形式的区别。BeanFactory和ApplicationContext都反对BeanPostProcessor、BeanFactoryPostProcessor的应用,但两者之间的区别是:BeanFactory须要手动注册,而ApplicationContext则是主动注册。

Bean注入容器有哪些形式?

1、@Configuration + @Bean

@Configuration用来申明一个配置类,而后应用 @Bean 注解,用于申明一个bean,将其退出到Spring容器中。

@Configurationpublic class MyConfiguration {    @Bean    public Person person() {        Person person = new Person();        person.setName("大彬");        return person;    }}

2、通过包扫描特定注解的形式

@ComponentScan搁置在咱们的配置类上,而后能够指定一个门路,进行扫描带有特定注解的bean,而后加至容器中。

特定注解包含@Controller、@Service、@Repository、@Component

@Componentpublic class Person {    //...} @ComponentScan(basePackages = "com.dabin.test.*")public class Demo1 {    public static void main(String[] args) {        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);        Person bean = applicationContext.getBean(Person.class);        System.out.println(bean);    }}

3、@Import注解导入

@Import注解平时开发用的不多,然而也是十分重要的,在进行Spring扩大时常常会用到,它常常搭配自定义注解进行应用,而后往容器中导入一个配置文件。

@ComponentScan/*把用到的资源导入到以后容器中*/@Import({Person.class})public class App {    public static void main(String[] args) throws Exception {        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);        System.out.println(context.getBean(Person.class));        context.close();    }}

4、实现BeanDefinitionRegistryPostProcessor进行后置解决。

在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 办法,就是等beanDefinition加载结束之后,对beanDefinition进行后置解决,能够在此进行调整IOC容器中的beanDefinition,从而烦扰到前面进行初始化bean。

在上面的代码中,咱们手动向beanDefinitionRegistry中注册了person的BeanDefinition。最终胜利将person退出到applicationContext中。

public class Demo1 {    public static void main(String[] args) {        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();        MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();        applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);        applicationContext.refresh();        Person bean = applicationContext.getBean(Person.class);        System.out.println(bean);    }} class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {    @Override    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();        registry.registerBeanDefinition("person", beanDefinition);    }        @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {    }}

5、应用FactoryBean接口

如下图代码,应用@Configuration + @Bean的形式将 PersonFactoryBean 退出到容器中,这里没有向容器中间接注入 Person,而是注入 PersonFactoryBean,而后从容器中拿Person这个类型的bean。

@Configurationpublic class Demo1 {    @Bean    public PersonFactoryBean personFactoryBean() {        return new PersonFactoryBean();    }     public static void main(String[] args) {        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);        Person bean = applicationContext.getBean(Person.class);        System.out.println(bean);    }} class PersonFactoryBean implements FactoryBean<Person> {    @Override    public Person getObject() throws Exception {        return new Person();    }    @Override    public Class<?> getObjectType() {        return Person.class;    }}

Bean的作用域

1、singleton:单例,Spring中的bean默认都是单例的。

2、prototype:每次申请都会创立一个新的bean实例。

3、request:每一次HTTP申请都会产生一个新的bean,该bean仅在以后HTTP request内无效。

4、session:每一次HTTP申请都会产生一个新的bean,该bean仅在以后HTTP session内无效。

5、global-session:全局session作用域。

Spring主动拆卸的形式有哪些?

Spring的主动拆卸有三种模式:byType(依据类型),byName(依据名称)、constructor(依据构造函数)。

byType

找到与依赖类型雷同的bean注入到另外的bean中,这个过程须要借助setter注入来实现,因而必须存在set办法,否则注入失败。

当xml文件中存在多个雷同类型名称不同的实例Bean时,Spring容器依赖注入依然会失败,因为存在多种适宜的选项,Spring容器无奈晓得该注入那种,此时咱们须要为Spring容器提供帮忙,指定注入那个Bean实例。能够通过<bean>标签的autowire-candidate设置为false来过滤那些不须要注入的实例Bean

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" /><!-- autowire-candidate="false" 过滤该类型 --><bean id="userDao2" autowire-candidate="false" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" /><!-- byType 依据类型主动拆卸userDao--><bean id="userService" autowire="byType" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

byName

将属性名与bean名称进行匹配,如果找到则注入依赖bean。

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" /><bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" /><!-- byName 依据名称主动拆卸,找到UserServiceImpl名为 userDao属性并注入--><bean id="userService" autowire="byName" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

constructor

存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型雷同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败。

@Autowired和@Resource的区别?

Autowire是spring的注解。默认状况下@Autowired是按类型匹配的(byType)。如果须要按名称(byName)匹配的话,能够应用@Qualifier注解与@Autowired联合。@Autowired 能够传递一个required=false的属性,false指明当userDao实例存在就注入不存就疏忽,如果为true,就必须注入,若userDao实例不存在,就抛出异样。

public class UserServiceImpl implements UserService {    //标注成员变量    @Autowired    @Qualifier("userDao1")    private UserDao userDao;    }

Resource是j2ee的注解,默认按 byName模式主动注入。@Resource有两个中重要的属性:name和type。name属性指定bean的名字,type属性则指定bean的类型。因而应用name属性,则按byName模式的主动注入策略,如果应用type属性,则按 byType模式主动注入策略。假使既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。

@Resource(name="userDao")private UserDao  userDao;//用于成员变量//也能够用于set办法标注@Resource(name="userDao")public void setUserDao(UserDao userDao) {   this.userDao= userDao;}

上述两种主动拆卸的依赖注入并不适宜简略值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的形式。

@Value和@Autowired、@Resource相似,也是用来对属性进行注入的,只不过@Value是用来从Properties文件中来获取值的,并且@Value能够解析SpEL(Spring表达式)。

比方,jdbc.properties文件如下:

jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=truejdbc.username=rootjdbc.password=root

利用注解@Value获取jdbc.url和jdbc.username的值,实现如下:

public class UserServiceImpl implements UserService {    //占位符形式    @Value("${jdbc.url}")    private String url;    //SpEL表达方式,其中代表xml配置文件中的id值configProperties    @Value("#{configProperties['jdbc.username']}")    private String userName;}

@Qualifier 注解有什么作用

当须要创立多个雷同类型的 bean 并心愿仅应用属性拆卸其中一个 bean 时,能够应用@Qualifier 注解和 @Autowired 通过指定应该拆卸哪个 bean 来打消歧义。

@Bean和@Component有什么区别?

都是应用注解定义 Bean。@Bean 是应用 Java 代码拆卸 Bean,@Component 是主动拆卸 Bean。

@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创立bean,每个类对应一个 Bean。

@Bean 注解用在办法上,示意这个办法会返回一个 Bean。@Bean 须要在配置类中应用,即类上须要加上@Configuration注解。

@Componentpublic class Student {    private String name = "lkm";     public String getName() {        return name;    }}@Configurationpublic class WebSocketConfig {    @Bean    public Student student(){        return new Student();    }}

@Bean 注解更加灵便。当须要将第三方类拆卸到 Spring 容器中,因为没方法源代码上增加@Component注解,只能应用@Bean 注解的形式,当然也能够应用 xml 的形式。

@Component、@Controller、@Repositor和@Service 的区别?

@Component:最一般的组件,能够被注入到spring容器进行治理。

@Controller:将类标记为 Spring Web MVC 控制器。

@Service:将类标记为业务层组件。

@Repository:将类标记为数据拜访组件,即DAO组件。

Spring 事务实现形式有哪些?

事务就是一系列的操作原子执行。Spring事务机制次要包含申明式事务和编程式事务。

  • 编程式事务:通过编程的形式治理事务,这种形式带来了很大的灵活性,但很难保护。
  • 申明式事务:将事务管理代码从业务办法中分离出来,通过aop进行封装。Spring申明式事务使得咱们无须要去解决取得连贯、敞开连贯、事务提交和回滚等这些操作。应用 @Transactional 注解开启申明式事务。

@Transactional相干属性如下:

属性类型形容
valueString可选的限定描述符,指定应用的事务管理器
propagationenum: Propagation可选的事务流传行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时工夫设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异样类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异样类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异样类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异样类名字数组

有哪些事务流传行为?

在TransactionDefinition接口中定义了七个事务流传行为:

  1. PROPAGATION_REQUIRED如果存在一个事务,则反对以后事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个办法都加了事务注解,并且运行在雷同线程中,则这两个办法应用雷同的事务中。如果运行在不同线程中,则会开启新的事务。
  2. PROPAGATION_SUPPORTS 如果存在一个事务,反对以后事务。如果没有事务,则非事务的执行。
  3. PROPAGATION_MANDATORY 如果曾经存在一个事务,反对以后事务。如果不存在事务,则抛出异样IllegalTransactionStateException
  4. PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。须要应用JtaTransactionManager作为事务管理器。
  5. PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。须要应用JtaTransactionManager作为事务管理器。
  6. PROPAGATION_NEVER 总是非事务地执行,如果存在一个流动事务,则抛出异样。
  7. PROPAGATION_NESTED 如果一个流动的事务存在,则运行在一个嵌套的事务中。如果没有流动事务, 则按PROPAGATION_REQUIRED 属性执行。

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:

应用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。

应用PROPAGATION_NESTED时,外层事务的回滚能够引起内层事务的回滚。而内层事务的异样并不会导致外层事务的回滚,它是一个真正的嵌套事务。

Spring事务在什么状况下会生效?

1.拜访权限问题

java的拜访权限次要有四种:private、default、protected、public,它们的权限从左到右,顺次变大。

如果事务办法的拜访权限不是定义成public,这样会导致事务生效,因为spring要求被代理办法必须是public的。

打开源码,能够看到,在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute办法中有个判断,如果指标办法不是public,则返回null,即不反对事务。

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {    // Don't allow no-public methods as required.    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {      return null;    }    ...}

2. 办法用final润饰

如果事务办法用final润饰,将会导致事务生效。因为spring事务底层应用了aop,也就是通过jdk动静代理或者cglib,帮咱们生成了代理类,在代理类中实现的事务性能。

但如果某个办法用final润饰了,那么在它的代理类中,就无奈重写该办法,而增加事务性能。

同理,如果某个办法是static的,同样无奈通过动静代理,变成事务办法。

3.对象没有被spring治理

应用spring事务的前提是:对象要被spring治理,须要创立bean实例。如果类没有加@Controller、@Service、@Component、@Repository等注解,即该类没有交给spring去治理,那么它的办法也不会生成事务。

4.表不反对事务

如果MySQL应用的存储引擎是myisam,这样的话是不反对事务的。因为myisam存储引擎不反对事务。

5.办法外部调用

如下代码所示,update办法下面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 办法,updateOrder 办法上的事务会生效。

因为产生了本身调用,调用该类本人的办法,而没有通过 Spring 的代理类,只有在内部调用事务才会失效。

@Servicepublic class OrderServiceImpl implements OrderService {    public void update(Order order) {        this.updateOrder(order);    }    @Transactional    public void updateOrder(Order order) {        // update order    }}

解决办法:

1、再申明一个service,将外部调用改为内部调用

2、应用编程式事务

3、应用AopContext.currentProxy()获取代理对象

@Servciepublic class OrderServiceImpl implements OrderService {       public void update(Order order) {        ((OrderService)AopContext.currentProxy()).updateOrder(order);   }    @Transactional    public void updateOrder(Order order) {        // update order    } }

6.未开启事务

如果是spring我的项目,则须要在配置文件中手动配置事务相干参数。如果忘了配置,事务必定是不会失效的。

如果是springboot我的项目,那么不须要手动配置。因为springboot曾经在DataSourceTransactionManagerAutoConfiguration类中帮咱们开启了事务。

7.吞了异样

有时候事务不会回滚,有可能是在代码中手动catch了异样。因为开发者本人捕捉了异样,又没有手动抛出,把异样吞掉了,这种状况下spring事务不会回滚。

如果想要spring事务可能失常回滚,必须抛出它可能解决的异样。如果没有抛异样,则spring认为程序是失常的。

Spring怎么解决循环依赖的问题?

首先,有两种Bean注入的形式。

结构器注入和属性注入。

对于结构器注入的循环依赖,Spring解决不了,会间接抛出BeanCurrentlylnCreationException异样。

对于属性注入的循环依赖(单例模式下),是通过三级缓存解决来循环依赖的。

而非单例对象的循环依赖,则无奈解决。

上面剖析单例模式下属性注入的循环依赖是怎么解决的:

首先,Spring单例对象的初始化大略分为三步:

  1. createBeanInstance:实例化bean,应用构造方法创建对象,为对象分配内存。
  2. populateBean:进行依赖注入。
  3. initializeBean:初始化bean。

Spring为了解决单例的循环依赖问题,应用了三级缓存:

singletonObjects:实现了初始化的单例对象map,bean name --> bean instance

earlySingletonObjects :实现实例化未初始化的单例对象map,bean name --> bean instance

singletonFactories : 单例对象工厂map,bean name --> ObjectFactory,单例对象实例化实现之后会退出singletonFactories。

在调用createBeanInstance进行实例化之后,会调用addSingletonFactory,将单例对象放到singletonFactories中。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {    Assert.notNull(singletonFactory, "Singleton factory must not be null");    synchronized (this.singletonObjects) {        if (!this.singletonObjects.containsKey(beanName)) {            this.singletonFactories.put(beanName, singletonFactory);            this.earlySingletonObjects.remove(beanName);            this.registeredSingletons.add(beanName);        }    }}

如果A依赖了B的实例对象,同时B也依赖A的实例对象。

  1. A首先实现了实例化,并且将本人增加到singletonFactories中
  2. 接着进行依赖注入,发现自己依赖对象B,此时就尝试去get(B)
  3. 发现B还没有被实例化,对B进行实例化
  4. 而后B在初始化的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects和二级缓存earlySingletonObjects没找到,尝试三级缓存singletonFactories,因为A初始化时将本人增加到了singletonFactories,所以B能够拿到A对象,而后将A从三级缓存中移到二级缓存中
  5. B拿到A对象后顺利完成了初始化,而后将本人放入到一级缓存singletonObjects中
  6. 此时返回A中,A此时能拿到B的对象顺利完成本人的初始化

由此看出,属性注入的循环依赖次要是通过将实例化实现的bean增加到singletonFactories来实现的。而应用结构器依赖注入的bean在实例化的时候会进行依赖注入,不会被增加到singletonFactories中。比方A和B都是通过结构器依赖注入,A在调用结构器进行实例化的时候,发现自己依赖B,B没有被实例化,就会对B进行实例化,此时A未实例化实现,不会被增加到singtonFactories。而B依赖于A,B会去三级缓存寻找A对象,发现不存在,于是又会实例化A,A实例化了两次,从而导致抛异样。

总结:1、利用缓存辨认曾经遍历过的节点; 2、利用Java援用,先提前设置对象地址,后欠缺对象。

Spring启动过程

  1. 读取web.xml文件。
  2. 创立 ServletContext,为 ioc 容器提供宿主环境。
  3. 触发容器初始化事件,调用 contextLoaderListener.contextInitialized()办法,在这个办法会初始化一个利用上下文WebApplicationContext,即 Spring 的 ioc 容器。ioc 容器初始化实现之后,会被存储到 ServletContext 中。
  4. 初始化web.xml中配置的Servlet。如DispatcherServlet,用于匹配、解决每个servlet申请。

Spring 的单例 Bean 是否有并发平安问题?

当多个用户同时申请一个服务时,容器会给每一个申请调配一个线程,这时多个线程会并发执行该申请对应的业务逻辑,如果业务逻辑有对单例状态的批改(体现为此单例的成员属性),则必须思考线程平安问题。

无状态bean和有状态bean

  • 有实例变量的bean,能够保留数据,是非线程平安的。
  • 没有实例变量的bean,不能保留数据,是线程平安的。

在Spring中无状态的Bean适宜用单例模式,这样能够共享实例进步性能。有状态的Bean在多线程环境下不平安,个别用Prototype模式或者应用ThreadLocal解决线程平安问题。

Spring Bean如何保障并发平安?

Spring的Bean默认都是单例的,某些状况下,单例是并发不平安的。

Controller 举例,如果咱们在 Controller 中定义了成员变量。当多个申请降临,进入的都是同一个单例的 Controller 对象,并对此成员变量的值进行批改操作,因而会相互影响,会有并发平安的问题。

应该怎么解决呢?

为了让多个HTTP申请之间不相互影响,能够采取以下措施:

1、单例变原型

对 web 我的项目,能够 Controller 类上加注解 @Scope("prototype")@Scope("request"),对非 web 我的项目,在 Component 类上增加注解 @Scope("prototype")

这种形式实现起来非常简单,然而很大水平上增大了 Bean 创立实例化销毁的服务器资源开销。

2、尽量避免应用成员变量

在业务容许的条件下,能够将成员变量替换为办法中的局部变量。这种形式集体认为是最失当的。

3、应用并发平安的类

如果非要在单例Bean中应用成员变量,能够思考应用并发平安的容器,如 ConcurrentHashMapConcurrentHashSet 等等,将咱们的成员变量包装到这些并发平安的容器中进行治理即可。

4、分布式或微服务的并发平安

如果还要进一步思考到微服务或分布式服务的影响,形式3便不适合了。这种状况下能够借助于能够共享某些信息的分布式缓存中间件,如Redis等。这样即可保障同一种服务的不同服务实例都领有同一份共享信息了。

@Async注解的原理

当咱们调用第三方接口或者办法的时候,咱们不须要期待办法返回才去执行其它逻辑,这时如果响应工夫过长,就会极大的影响程序的执行效率。所以这时就须要应用异步办法来并行执行咱们的逻辑。在springboot中能够应用@Async注解实现异步操作。

应用@Async注解实现异步操作的步骤:

1.首先在启动类上增加 @EnableAsync 注解。

@Configuration@EnableAsyncpublic class App {    public static void main(String[] args) {         ApplicationContext ctx = new               AnnotationConfigApplicationContext(App.class);        MyAsync service = ctx.getBean(MyAsync.class);        System.out.println(service.getClass());        service.async1();        System.out.println("main thread finish...");    }}

2.在对应的办法上增加@Async注解。

@Componentpublic class MyAsync {    @Async    public void asyncTest() {        try {            TimeUnit.SECONDS.sleep(20);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("asyncTest...");    }}

运行代码,控制台输入:

main thread finish...asyncTest...

证实asyncTest办法异步执行了。

原理:

咱们在主启动类上贴了一个@EnableAsync注解,能力应用@Async失效。@EnableAsync的作用是通过@import导入了AsyncConfigurationSelector。在AsyncConfigurationSelector的selectImports办法将ProxyAsyncConfiguration定义为Bean注入容器。在ProxyAsyncConfiguration中通过@Bean的形式注入AsyncAnnotationBeanPostProcessor类。

代码如下:

@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {}public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {    public String[] selectImports(AdviceMode adviceMode) {        switch (adviceMode) {            case PROXY:                return new String[] { ProxyAsyncConfiguration.class.getName() };            //...        }    }}public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {        //创立postProcessor        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();        //...    }}

AsyncAnnotationBeanPostProcessor往往期创立了一个增强器AsyncAnnotationAdvisor。在AsyncAnnotationAdvisor的buildAdvice办法中,创立了AnnotationAsyncExecutionInterceptor。

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {    @Override    public void setBeanFactory(BeanFactory beanFactory) {        super.setBeanFactory(beanFactory);        //创立一个增强器        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);        //...        advisor.setBeanFactory(beanFactory);        this.advisor = advisor;    }}public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {    public AsyncAnnotationAdvisor(            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {        //加强办法        this.advice = buildAdvice(executor, exceptionHandler);        this.pointcut = buildPointcut(asyncAnnotationTypes);    }    // 委托给AnnotationAsyncExecutionInterceptor拦截器    protected Advice buildAdvice(            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {        //拦截器        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);        interceptor.configure(executor, exceptionHandler);        return interceptor;    }}

AnnotationAsyncExecutionInterceptor继承自AsyncExecutionInterceptor,间接实现了MethodInterceptor。该拦截器的实现的invoke办法把原来办法的调用提交到新的线程池执行,从而实现了办法的异步。

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {    public Object invoke(final MethodInvocation invocation) throws Throwable {        //...        //构建放到AsyncTaskExecutor执行Callable Task        Callable<Object> task = () -> {            //...        };        //提交到新的线程池执行        return doSubmit(task, executor, invocation.getMethod().getReturnType());    }}

由下面剖析能够看到,@Async注解其实是通过代理的形式来实现异步调用的。

那应用@Async有什么要留神的呢?

1.应用@Aysnc的时候最好配置一个线程池Executor以让线程复用节俭资源,或者为SimpleAsyncTaskExecutor设置基于线程池实现的ThreadFactory,在否则会默认应用SimpleAsyncTaskExecutor,该executor会在每次调用时新建一个线程。

2.调用本类的异步办法是不会起作用的。这种形式绕过了代理而间接调用了办法,@Async注解会生效。


最初给大家分享一个Github仓库,下面有大彬整顿的300多本经典的计算机书籍PDF,包含C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,能够star一下,下次找书间接在下面搜寻,仓库继续更新中~

Github地址