spring
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
什么是spring
Spring 是个 java 企业级应用的开源开发框架。Spring 主要用来开发 Java 应用,但是有些扩展是针对构建 J2EE 平台的 web 应用。Spring 框架目标是简化 Java企业级应用开发,并通过 POJO 为基础的编程模型促进良好的编程习惯。
spring好处优势
1.轻量:Spring 是轻量的,基本的版本大约 2MB。
2.控制反转:Spring 通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
3.面向切面的编程(AOP):Spring 支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
4.容器:Spring 包含并管理应用中对象的生命周期和配置。
5.MVC 框架:Spring 的 WEB 框架是个精心设计的框架,是 Web 框架的一个很好的替代品。
6.事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
7.异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 JDBC,HibernateorJDO 抛出的)转化为一致的 unchecked 异常。
谈谈自己对于 Spring IOC 和 AOP 的理解
IOC
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
AOP
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
如何理解 Spring 中的 AOP ?
在介绍 AOP 之前,我们先来了解一下 OOP 的概念。OOP 又指面向对象,它允许开发者定义纵向的关系,但并不适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。
AOP,又被称为面向切面,它是作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为切面(Aspect)。
AOP 减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。
AOP 实现的关键在于代理模式,AOP 代理主要分为静态代理和动态代理。静态代理的代表为AspectJ,而动态代理则以 Spring AOP 为代表。
AspectJ 与 Spring AOP 的区别?
- AspectJ 是静态代理的增强,所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,它会在编译阶段将切面织入到 Java 字节码中,运行的时候就是增强之后的 AOP 对象。
- Spring AOP 使用的是动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。(Spring AOP中的动态代理主要有两种方式, JDK 动态代理和 CGLIB 动态代理)
BeanFactory 和FactoryBean
BeanFactory 对底层的接口
表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。
是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中 XmlBeanFactory,ApplicationContext 等具体的容器都是实现了BeanFactory,再在其基础之上附加了其他的功能。
BeanFactory和ApplicationContext就是spring框架的两个IOC容器,现在一般使用ApplicationContext,其不但包含了BeanFactory的作用,同时还进行更多的扩展。
BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。
原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。ApplicationContext接口,它由BeanFactory接口派生而来,
ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先
FactoryBean 一般这两个比没啥意义 这个也是个接口 就是个Bean
所有的bean都要有BeanFactory(也就是IOC容器)来进行管理。对于FactoryBean,是一个能生产或修饰对象生成的工厂bean,他的实现与设计模式中的工厂模式和修饰器模式类似。
FactoryBean可以说为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式
ApplicationContext继承了BeanFactory接口
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
-
国际化(MessageSource)
-
访问资源,如URL和文件(ResourceLoader)
-
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
-
消息发送、响应机制(ApplicationEventPublisher)
-
AOP(拦截器)
BeanFactory 和ApplicationContext区别
BeanFactory: 启动时候不会去实例化Bean, 去容器中拿bean的时候才会去实例化;也就是延迟实例化
优点:应用启动的时候占用资源很少;对资源要求较高的应用,比较有优势;
ApplicationContext:启动时候就会把所有的bean全部实例化。
优点:
1.所有的Bean在启动的时候都加载,系统运行的速度快;
2.在启动的时候所有的Bean都加载了,我们就能在系统启动的时候,尽早的发现系统中的配置问题
3.建议web应用,在启动的时候就把所有的Bean都加载了。(把费时的操作放到系统启动中完成)
ApplicationContext的三个实现类:
ClassPathXmlApplication
:把上下文文件当成类路径资源。FileSystemXmlApplication
:从文件系统中的 XML 文件载入上下文定义信息。XmlWebApplicationContext
:从Web系统中的XML文件载入上下文定义信息。
spring中bean的作用域有哪些
1.单例模式(spring默认机制)
<!--<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神"/>-->
<bean id="user2" class="com.kuang.pojo.User" c:_0="狂神" c:_1="22" scope="singleton"/>
2.原型模式 每次从容器中get的时候,都会产生一个新对象!
<!--<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神"/>-->
<bean id="user2" class="com.kuang.pojo.User" c:_0="狂神" c:_1="22" scope="prototype"/>
-
singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。也就是单例模式。
-
prototype : 每次请求都会创建一个新的 bean 实例。
-
request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
-
session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
-
global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话
-
(1)singleton就是在创建起容器时(也就是加载spring配置文件的时候)就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。
(2)当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。 在XML中将bean定义成prototype,可以这样配置:
3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同
spring中bean的线程安全。
大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。
常见的有两种解决办法:
- 在Bean对象中尽量避免定义可变的成员变量(不太现实)。
- 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
bean生命周期(也就是bean的创建和销毁)
![img](https://i-blog.csdnimg.cn/blog_migrate/98abfae4af34de3e8a7f30150fa3e804.png)
从创建对象到对象销毁的过程
①. 通过构造器或工厂方法创建 Bean 实例
②. 为 Bean 的属性设置值和对其他 Bean 的引用
③ . 将 Bean 实 例 传 递 给 Bean 前置 处 理 器 的 postProcessBeforeInitialization 方法
④. 调用 Bean 的初始化方法(init-method)
⑤ . 将 Bean 实 例 传 递 给 Bean 后 置 处 理 器 的 postProcessAfterInitialization 方法
⑦. Bean 可以使用了
⑧. 当容器关闭时, 调用 Bean 的销毁方法(destroy-method)
IOC创建对象的三种方式
- 通过无参构造方法(需要用到无参构造和setter方法)
- 通过有参构造(用到有参构造,不需要setter方法)
- 通过工厂方法来创建对象,用的很少
1、调用无参构造
<bean id="user" class="edu.cloud.spring.entity.User"></bean>
2、调用带参构造
<bean id="user2" class="edu.cloud.spring.entity.User">
<constructor-arg value="tom" index="1"></constructor-arg>
<constructor-arg value="101" index="0"></constructor-arg>
</bean>
3、工厂创建对象
工厂类:非静态方法创建对象
装配bean和依赖注入
https://blog.csdn.net/qq_41738264/article/details/105533760
装配bean的三种方式及区别
创建应用对象之间协作关系的行为称为装配, 这也是依赖注入的本质.
Spring提供了3种装配机制, 它们分别为:
- 在XML中进行显式配置
- 在Java中进行显式配置
- 隐式的Bean发现机制和自动装配
https://blog.csdn.net/dela_?t=1
依赖注入的四种方式
- 构造器
- setter方法
- 静态工厂:通过调用工厂类中定义的静态 方法来获取 对象
- 实例工厂:获取对象实例的方法是非静态的
1、通过构造
<bean id="user2" class="edu.cloud.spring.entity.User">
<constructor-arg value="tom" index="1"></constructor-arg>
<constructor-arg value="101" index="0"></constructor-arg>
</bean>
2、通过set方法对属性注入【常用】
<bean id="user4" class="edu.cloud.spring.entity.User">
<property name="id" value="110"></property>
<property name="name" value="hhh"></property>
</bean>
<!-- 不用value了,用rel把这个对象引用进来 -->
<!-- 三层架构 依赖注入 -->
<!-- Action -->
<bean id="userAction" class="edu.cloud.second.UserAction">
<!-- property中的name指的是命名setter方法中的attribute部分:eg. setUserService() name为:userService -->
<property name="userService" ref="userService"></property>
</bean>
<!-- UserService -->
<bean id="userService" class="edu.cloud.second.UserService">
<!-- 不用value了,用rel把这个对象引用进来 -->
<property name="userDao" ref="userDao"></property>
</bean>
<!-- UserDao -->
<bean id="userDao" class="edu.cloud.second.UserDao"></bean>
<!-- 内部Bean -->
<bean id="userAction2" class="edu.cloud.second.UserAction">
<property name="userService">
<bean class="edu.cloud.second.UserService">
<property name="userDao">
<bean class="edu.cloud.second.UserDao"></bean>
</property>
</bean>
</property>
</bean>
3、P名称空间,属性注入优化
xml头文件包含:xmlns:p="http://www.springframework.org/schema/p"
<!-- p:属性注入 -->
<bean id="user" class="edu.cloud.second.User" p:id="999" p:name="Jack"></bean>
<!-- p:set方法注入 ref 引用-->
<bean id="userDao" class="edu.cloud.second.UserDao"></bean>
<bean id="userService" class="edu.cloud.second.UserService" p:userDao-ref="userDao"></bean>
<bean id="userAction2" class="edu.cloud.second.UserAction" p:userService-ref="userService"></bean>
转配bean方式可以分为手动装配和自动装配:
手动装配分为基于java和xml
bean的自动装配的五种方式:
no:关闭自动装配,通过显示设置ref属性进行对象装配:
byName:
byType:
constructor:
autodetect:首先尝试constructor,再尝试byType
xml
正常方式:
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="people" class="com.kuang.pojo.People" >
<property name="name" value="zhenxing"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
</bean>
byName
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="people" class="com.kuang.pojo.People" autowire="byName"/>
byType
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="people" class="com.kuang.pojo.People" autowire="byType"/>
javaConfig 使用注解自动装配
@Autowired pojo中的People里面
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
@Resource注解和@Autowired对比:
@Resource是Java的注解,@Autowired是spring的注解
- @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
@Resource装配顺序:
-
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
-
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
-
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
-
④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
注解开发
@Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层!
- dao [@Repository], 一般dao层习惯用这个注解 作为持久层操作数据库的bean来使用
- service 【@Service】一般service层习惯用这个注解
- controller 【@Controller】一般controller层用这个注解 会被spring-mvc框架所使用
- @Component可以标注任意类为spring组件,如果一个bean不知道属于哪个层,可以使用@Component注解标注。
controller和repository不能被代替
静态代理模式的好处:
代理模式的好处:
-
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
-
公共也就就交给代理角色!实现了业务的分工!
-
公共业务发生扩展的时候,方便集中管理!
缺点:
-
一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低。
动态代理(动态代理的关系是在运行期确定的)
静态代理缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类.同时,一旦接口增加方法,目标对象与代理对象都要维护。
解决办法:动态代理:其实动态代理和静态代理的思想是不变的,动态代理和静态代理的区别就是,动态代理不用我们去手编写代理类,在运行时,动态的在内存中生产代理类。(字节码对象级别的代理对象)。
动态代理的底层都是反射
- 动态代理和静态代理角色相同
- 动态代理的代理类是动态生成的,不是我们直接写好的。
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口–JDK动态代理
- 基于类:cglib
- Java字节码实现
通过接口InvocationHandler和类proxy
AOP两种代理方式
JDK动态代理:基于接口
CGLib动态代理:基于类
JDK只能为接口创建代理实例,对于没有通过接口定义业务方法的类,只能通过CGLib创建动态代理实现
JDK和CGLib动态代理区别
1、JDK动态代理具体实现原理:
通过实现InvocationHandler接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
4、使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
Spring 如何处理线程并发问题?
在一般情况下,只有无状态的 Bean 才可以在多线程环境下共享,在 Spring 中,绝大部分 Bean 都可以声明为 singleton 作用域,因为 Spring 对一些 Bean 中非线程安全状态采用 ThreadLocal 进行处理,解决线程安全问题。
AOP的五种类型:
前置通知,后置通知,成功通知,异常通知,环绕通知
Spring 框架中用到了哪些设计模式?
- 工厂模式:BeanFactory 就是简单工厂模式的体现,用来创建对象的实例
- 单例模式:Bean 默认为单例模式
- 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术
- 模板方法:用来解决代码重复的问题,比如 RestTemplate
- 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 – ApplicationListener
spring事务相关
什么是事务:
数据库操作的最小单元
事务四大特性 ;ACID
spring管理事务的方式
1.编程式事务管理
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
2.声明式事务管理 用的多
声明式事务又分为两种:
- 基于XML的声明式事务
- 基于注解的声明式事务
声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
spring有哪几种事务传播行为
支持当前事务的情况:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不支持当前事务的情况:
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,挂起存在的任何事务。
- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
spring事务隔离级别
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读、幻读
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是不可重复读、幻读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
springboot
自动装配原理
简洁版
面试官问你你可以这样说,springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refreshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,这个里面还有一句@Import(AutoConfigurationImportSelector.class)
, 里面有个核心方法getCandidateConfigurations
会调用loadFactoryNames(获取所有的加载配置,实现在下面),根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解(@ConditionalOn…可能是class,property),先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器.
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
所有的自动配置类就都在这里。
思考个问题,为什么这么多的自动配置有的没有生效,需要导入对应的starter才有作用
public class SpringApplicationAdminJmxAutoConfiguration:
动手实现自己的starter
1、新建组件com-itpsc-service,组件只有一个service。
2、编写UserService类
3、打包发布组件到maven仓库
4、新建一个启动组件starter,这个start里面会引入service的依赖
5、编写自动配置类
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(JmxAutoConfiguration.class)
@ConditionalOnClass()
public class UserAutoConfiguration {
@Bean
public UserService getBean(UserProperties userProperties) {
//创建组件实例
UserService userService = new UserService();
------
return userService;
}
}
6、配置spring.factories文件
在src/main/resources新建META-INF文件夹,在META-INF文件夹下新建spring.factories文件。配置内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.itpsc.spring.boot.starter.UserAutoConfiguration
7、打包发布starter
8、新建工程引入starter就可以了。
配置文件内容
这个核心依赖只是版本而已,需要哪一个还需要去pom去写
依赖版本:
主程序
springbootapplication
springbootapplication 标注这更是一个springboot的应用
- springbootconfiguration //是个配置类
- enableautoconfiguration //自动配置
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
- comonentscan //
spring.factories
**springboot的所有自动配置都是在启动的时候扫描并加载:spring.factories 所有的自动配置类都在这里面,还要判断条件是否成立,只要导入对应的stater,就会有对应的启动器,有了启动器,我们自动装配就会失效,然后就配置 **
springmvc
什么是MVC
- MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。
- 是将业务逻辑、数据、显示分离的方法来组织代码。
- MVC主要作用是降低了视图与业务逻辑间的双向偶合。
- MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
最典型的MVC就是JSP + servlet + javabean的模式。
springMVC原理图
面试回答:前端控制器接受用户的请求并拦截,根据对应的请求找到对应的Controller处理器,处理器调用业务层并返回信息给前端控制器,然后前端控制器调用视图解析器找到对应视图并将数据渲染
我们只需要做两个地方,controller业务层,视图返回的名字
下面是文字步骤说明:
1、用户发送请求到前端控制器(DispatcherServlet)。
2、前端控制器请求处理器映射器(HandlerMapping)去查找处理器(Handler),HandlerMapping根据请求url查找Handler。
3、HandlerExcution将解析后的信息传递给DispatcherServlet
4、前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)。
5、Handler让具体的controller去执行,由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
6、处理器执行完给处理器适配器返回ModelAndView。
7、处理器适配器向前端控制器返回ModelAndView。
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器去进行视图解析。
9、视图解析器向前端控制器返回View。
10、前端控制器对视图进行渲染。
11、前端控制器向用户响应结果。
![springmvc工作原理](https://i-blog.csdnimg.cn/blog_migrate/b0c9000d8024dd87067f8b489736c573.png)
springmvc注解
1 @Controller
控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示
@Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean,Dispatch Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上,需要注意的是,在Spring MVC声明控制器Bean的时候,只能使用@Controller。
2 @RequestMapping(就是访问路径和参数)
@RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。它可以注解在类和方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径,@RequestMapping支持Servlet的request和response作为参数,也支持对它们的媒体类型进行配置。
3 @ResponseBody 返回JSON数据
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们很多机遇Ajax的程序,可以以此注解返回数据而不是返回页面;此注解可以放在返回值或者方法上。
4 @RequestBody 接收JSON数据
@RequestBody接收前端传来的实体,比如前端通过 JSON 提交传来两个参数 username 和 password,此时我们需要在后端封装一个实体来接收。在传递的参数比较多的情况下,使用 @RequestBody 接收会非常方便
5 @PathVariable 接收路径参数
@PathVariable 用来接收路径参数,如/news/001,可接收001作为参数,此注解放置在参数前。RestFul风格
6 @RestController
@RestController是一个组合注解,组合了@Controller和@ResponseBody,意味着当只开发一个和页面交互数据的控制的时候,需要使用此注解。 若没有此注解,要想实现上述功能,则需自己在代码中加@Controller和@ResponseBody两个注解。
7.@RequestParam
@RequestParam 注解顾名思义,也是获取请求参数的,上面我们介绍了 @PathValiable 注解也是获取请求参数的,那么 @RequestParam 和 @PathVariable 有什么不同呢?
spring用到的设计模式
单例模式 必须会写
在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。
使用单例模式的好处:
- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
- 由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。
饿汉式
// 饿汉式单例模式 静态变量
public class Hungry {
public static void main(String[] args) {
Hungry instance1 = Hungry.getInstance();
Hungry instance2 = Hungry.getInstance();
System.out.println(instance1 == instance2);
}
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
DCL懒汉式
package com.kuang.designModel.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* 用的时候再去加载 ->懒汉式单例
*/
public class LazyMan {
private static boolean qinjiang = false;
//构造器私有方法
private LazyMan() {
//此时反射不可能再破坏这个
synchronized (LazyMan.class){
if(qinjiang == false){
qinjiang = true;
}else {
throw new RuntimeException("不要用反射破坏异常");
}
}
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){
//加锁
//双重检测锁模式的 懒汉式单例 简称DCL
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();//不是原子型操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 可能发生指令重排序 所以上面需要加volatile
*/
}
}
}
return lazyMan;
}
//多线程并发
public static void main(String[] args) throws Exception {
Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
qinjiang.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//无视构造器
LazyMan instance = declaredConstructor.newInstance();
qinjiang.set(instance, false);
LazyMan instance1 = declaredConstructor.newInstance();
System.out.println(instance == instance1);//false 所以反射可以破坏单例模式
}
}
单例模式不安全,反射可以破坏
所以有了枚举枚举
/**
* 静态内部类 实现
*/
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
public static void main(String[] args) throws Exception {
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
原型模式Prototype
克隆
工厂模式
代理模式
静态代理
1.接口
2.真实角色
3.代理角色
4.客户端访问
代理模式可以使真是角色的操作更加纯粹,不用关注一些公共业务
公共也就交给了代理角色,实现了业务分工
公共业务发生拓展的时候,方便集中管理
缺点:
一个真是角色就会产生一个代理角色,开发效率变低。
动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 -JDK动态代理
- 基于类:cglib
- java字节码实现
模板方法
Spring 中 jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
适配器模式
mybatis
缓存
一级缓存
一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。
二级缓存
二级缓存是mapper级别的,Mybatis默认是没有开启二级缓存的。
#{}和${}区别
#{}:SQL参数占位符替换为?,可以防止SQL注入,变量替换后,会自动加上单引号
${}:拼接符,不能防止SQL注入,变量替换后,不会加上单引号,也可以表示properties文件中的变量占位符
动态sql 都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
答:Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能,Mybatis 提供了 9 种动态 sql 标签 trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能