IOC
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.2</version>
</dependency>
加载bean
public class BeanFactory {
private static Map container = new HashMap();
static {
//1:加载beans.xml配置文件
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//2:解析xml文件(dom4j)
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(inputStream);
Element beans = document.getRootElement();
//获取根标签的子节点
List<Element> elements = beans.elements();
for (Element element : elements) {
String idVal = element.attributeValue("id");
String classVal = element.attributeValue("class");
//实例化对象( //3:通过反射机制,去实例化对象)
Class<?> clazz = Class.forName(classVal);
Object obj = clazz.newInstance();
//存放到map集合中
container.put(idVal, obj);
}
}catch (Exception e) {
e.printStackTrace();
}
}
public static <T extends Object> T getBean (String beanName,Class<T> beanType){
Object obj = container.get(beanName);
if (obj == null) {
throw new RuntimeException("没有该对象");
}
return (T)obj;
}
DI
基本数据类型
<bean id="stu" class="com.qf.entity.Student"> <!-- 依赖spring注入属性值 set方法注入 stu.setName("张三")--> <!-- <property name="name" value="张三"/>--> <!-- <property name="age" value="18"/>--> <!-- 依赖spring注入属性值 通过构造方法注入--> <constructor-arg value="张三" name="name"/> <constructor-arg value="18" name="age"/> </bean>
集合类型
<!-- 依赖注入list --> <property name="courses"> <list> <value>java</value> <value>php</value> </list> </property> <!-- 依赖注入map --> <property name="data"> <map> <entry key="a" value="avalue"></entry> <entry key="b" value="bvalue"></entry> </map> </property> <!-- 依赖注入set --> <property name="setData"> <set> <value>1</value> <value>1</value> <value>1</value> <value>1</value> <value>2</value> </set> </property>
引用类型
一个bean(spring管理) 依赖另外一个bean(spring管理)
1:外置的bean
<!-- 配置orderservice--> <!--<bean id="orderService" class="com.qf.service.OrderService">--> <!-- <!– orderService.setOrderDao(orderDao1) –>--> <!-- <property name="orderDao" ref="orderDao1"></property>--> <!--</bean>--> <!--<!–配置orderDao–>--> <!--<bean id="orderDao1" class="com.qf.dao.OrderDao"></bean>-->
2:内置的bean
<bean id="orderService" class="com.qf.service.OrderService"> <!-- orderService.setOrderDao(orderDao1) --> <property name="orderDao" > <bean id="orderDao1" class="com.qf.dao.OrderDao"></bean> </property> </bean>
Bean作用域
> spring默认的bean的作用域是单例的(singleton)
>
> 如果scope="singleton" ,当容器初始化的时候,立即完成这个bean的实例化
>
> ```
> <bean id="stu" class="com.qf.entity.Student" scope="singleton">
> ```
多例?
>
> 如果scope="prototype" ,当容器初始化的时候,这个bean不会立即实例化,在getBean的时候实例化
>
> ```
> <bean id="stu" class="com.qf.entity.Student" scope="prototype">
自动装配
bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--第一步:开启注解扫描功能-->
<context:annotation-config/>
<!--第二步:扫描包的方式去扫描组件-->
<context:component-scan base-package="com.qf" />
<!--<bean id="orderService" class="com.qf.service.OrderService" autowire="byName" />-->
<!--<bean id="orderDao" class="com.qf.dao.OrderDao"></bean>-->
</beans>
注解
@Componet 组件,通用注解
@Service service层的注解
@Repository 持久层的注解
//@Configuration等价于beans.xml文件
ComponentScan等价于<context:component-scan base-package="com.qf.dao,com.qf.service"/>
PropertySource等价于<context:property-placeholder location="constant.properties"/>
@PropertySource("constant.properties")
//等价于<bean></bean>
@Bean
public Student getStudent(){
Student student = new Student();
student.setUsername("rose");
return student;
}
DI注解
@Autowired默认按类型装配(这个注解是属业spring的)【常用】
注意:根据类型自动注入,如果容器中该类型有多个,那么会抛异常
@Autowired,@Qualifier:通过这两个注解,可以指定name进行自动注入
@Resource(这个注解属于J2EE的)
1. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
2. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
3. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则为类型进行匹配,如果匹配则自动装配,如果多个则抛异常
@Value:主要是在组件中自动注入外部属性文件中的属性值
注意:必须在beans.xml中去加载外部属性文件
动态代理-jdk
> 前提:被代理对象必须实现接口
> Proxy.ProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
动态代理-cglib
> cgLib的动态代理 被代理对象不需要实现接口
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
SpringAOP
第一步:pom依赖
```xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置被代理对象 -->
<bean id="orderService" class="com.qf.service.OrderService"></bean>
<!--配置advice-->
<bean id="myAdvice" class="com.qf.service.MyAdvice"></bean>
<!-- 配置aop-->
<aop:config>
<!-- 动作:将增强的逻辑加入到切入点(织入) -->
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut="execution(public void com.qf.service.OrderService.saveOrder())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
完全注解
@Configuration
@ComponentScan(basePackages = {"com.qf"})
@EnableAspectJAutoProxy//开启aop注解开发
配置mybatis
配置sqlsessionfactorybean,会产 生一个sqlsessionfactory,这是个时候就需要加载mybatis核心配置文件mybatis-config。同时还需要mapperscanerconfigurer,将sqlsessionfactory交给他,同时需要把mapper包在哪告诉他,扫描后会进行代理实现将每一个接口实现,同时注入ioc容器
<context:annotation-config/>
<context:component-scan base-package="com.qf.service"/>
<!-- 加载属性配置文件-->
<context:property-placeholder location="db.properties"/>
<!-- 配置druid datasource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${url}" />
<property name="username" value="${username1}" />
<property name="password" value="${password1}" />
</bean>
<!-- 接管sqlsessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- <property name="configLocation" value="mybatis-config.xml"/>-->
<property name="dataSource" ref="dataSource"></property>
<!--加载映射文件 -->
<property name="mapperLocations" value="mappers/*.xml"></property>
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor"/>
</array>
</property>
<!-- 别名扫描 -->
<property name="typeAliasesPackage" value="com.qf.entity"/>
<!-- 设置信息 -->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true"/>
</bean>
</property>
//编程式事务
public void save(){
//开启事务
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);//设置隔离级别
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); //设置事务的传播行为
TransactionStatus status = manager.getTransaction(definition);
try {
//1:添加jack
Student student = new Student("jack", "888", new Date());
studentMapper.saveStudent(student);
//2:添加rose
Student student1 = new Student("rose", "999", new Date());
studentMapper.saveStudent(student1);
int i = 1/0;
//提交事务
manager.commit(status);
} catch (Exception exception) {
exception.printStackTrace();
//回滚事务
manager.rollback(status);
}
}
Spring如何管理事务?
-
编程事务处理:提供了transactiontemplate模板,利用改模板通过编程方式实现事务管理,缺点编写的麻烦,好处事务管理控制范围更为精准
声明式事务处理:通过声明的方式,只需要在需要做事务增强的方法上利用transactional注解(启动事务功能),声明事务特征即可
Spring的事务传播方式有哪些?
事务传播类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,则新建一个事务;如果已存在一个事务,则加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,则以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,则抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,则把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行操作,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
Spring的事务如何配置,常用注解有哪些?
对于声明式事务,是使用@Transactional进行标注的。这个注解可以标注在类或者方法上。
-
当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能。
-
当它标注在方法上时,代表这个方法将启用事务功能。
另外,在@Transactional注解上,我们可以使用isolation属性声明事务的隔离级别,使用propagation属性声明事务的传播机制。
Spring
FactoryBean:该接⼝以 bean 样式定义,但是它不是⼀种普通的 bean,它是个⼯⼚ bean,实现该 接⼝的类可以⾃⼰定义要创建的 bean 实例,只需要实现它的 getObject ⽅法即可。
说一说你对Spring容器的了解
Spring主要提供了两种类型的容器:BeanFactory和ApplicationContext。
-
BeanFactory:Spring 是基础类型的IoC容器,本质是个⼯⼚类,⽤于管理 bean 的⼯⼚,最 核⼼的功能是加载 bean,也就是 getBean ⽅法,通常我们不会直接使⽤该接⼝,⽽是使⽤其⼦接 ⼝。
-
ApplicationContext:包含 BeanFactory 的所有功能,还提供了其他⾼级的特性,⽐如:事件发布、国际化信息⽀持、统⼀ 资源加载策略等。正常情况下,我们都是使⽤的 ApplicationContext
2.4 说一说你对Spring IOC的理解
ioc原理
ioc 解决搞耦合,是通过di实现的,通过工厂模式利用反射并接卸xml文件,避免接口修改
加载xml配置文件,解析xml文件,拿到xml文档,拿beans根元素,获取根标签子节点(id,class)实例化对象 class。forname,存放2map集合,用工厂
说到IoC就不得不说DI(Dependency Injection),DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观,所以很多时候我们就用DI来代替它,
在具体的实现中,主要由三种注入方式:
-
构造方法注入
就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
-
setter方法注入
通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。
-
接口注入
相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和烦琐。
总体来说,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使用最多的注入方式。而接口注入因为侵入性较强,近年来已经不流行了。
Spring是如何管理Bean的?
Spring通过IoC容器来管理Bean,我们可以通过XML配置或者注解配置,来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多,所以现在大多时候会使用注解配置的方式。
以下是管理Bean时常用的一些注解:
-
@ComponentScan用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些特定的类是被排除在外的。
-
@Component、@Repository、@Service、@Controller
-
@Autowired、@Qualifier。
2.6 介绍Bean的作用域
参考答案
默认情况下,Bean在Spring容器中是单例的,我们可以通过@Scope注解修改Bean的作用域。该注解有如下5个取值,它们代表了Bean的5种不同类型的作用域:
类型 | 说明 |
---|---|
singleton | 在Spring容器中仅存在一个实例,即Bean以单例的形式存在。 |
prototype | 每次调用getBean()时,都会执行new操作,返回一个新的实例。 |
request | 每次HTTP请求都会创建一个新的Bean。 |
session | 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。 |
globalSession | 同一个全局的Session共享一个Bean,一般用于Portlet环境。 |
2.8 Spring是怎么解决循环依赖的?
参考答案
spring对循环依赖的处理有三种情况:
-
构造器的循环依赖:这种依赖spring是处理不了的,直接抛出BeanCurrentlylnCreationException异常。
-
单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖。
-
非单例循环依赖:无法处理。
接下来,我们具体看看spring是如何处理第二种循环依赖的。
Spring单例对象的初始化大略分为三步:
-
createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象;
-
populateBean:填充属性,这一步主要是多bean的依赖属性进行填充;
-
initializeBean:调用spring xml中的init 方法。
使用了三级缓存。
这三级缓存的作用分别是:
-
singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存);
-
earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级缓存);
-
singletonObjects:完成初始化的单例对象的cache(一级缓存)。
2.11 说一说你对Spring AOP的理解
参考答案
AOP(Aspect Oriented Programming)是面向切面编程,它是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
AOP的术语:
-
连接点(join point):对应的是具体被拦截的对象,因为Spring只能支持方法,所以被拦截的对象往往就是指特定的方法,AOP将通过动态代理技术把它织入对应的流程中。
-
切点(point cut):有时候,我们的切面不单单应用于单个方法,也可能是多个类的不同方法,这时,可以通过正则式和指示器的规则去定义,从而适配连接点。切点就是提供这样一个功能的概念。
-
通知(advice):就是按照约定的流程下的方法,分为前置通知、后置通知、环绕通知、事后返回通知和异常通知,它会根据约定织入流程中。
-
目标对象(target):即被代理对象。
-
引入(introduction):是指引入新的类和其方法,增强现有Bean的功能。
-
织入(weaving):它是一个通过动态代理技术,为原有服务对象生成代理对象,然后将与切点定义匹配的连接点拦截,并按约定将各类通知织入约定流程的过程。
-
切面(aspect):是一个可以定义切点、各类通知和引入的内容,SpringAOP将通过它的信息来增强Bean的功能或者将对应的方法织入流程。
Spring AOP: