Spring学习总结 + 【手写Spring底层机制核心】
  5DfGM4DuibK0 2024年04月09日 15 0

Spring学习总结

Spring基本介绍

Spring 学习的核心内容

1.IOC: 控制反转, 可以管理java 对象

2.AOP : 切面编程

3.JDBCTemplate : 是spring 提供一套访问数据库的技术, 应用性强,相对好理解

4.声明式事务: 基于ioc/aop 实现事务管理

spring价值

Spring 最大的价值,通过配置,给程序提供需要使用的
web 层[Servlet(Action/Controller)]/Service/Dao/[JavaBean/entity]对象,
这个是核心价值所在,也是ioc 的具体体现, 实现解耦.

IOC容器

对比传统模式

  • 传统的开发模式[JdbcUtils / 反射]

​ 1.程序员编写程序, 在程序中读取配置信息

​ 2.创建对象, new Object???() // 反射方式

​ 3.使用对象完成任务

  • IOC 的开发模式

    1、Spring 根据配置文件xml/注解, 创建对象, 并放入到容器(ConcurrentHashMap)中,
    并且可以完成对象之间的依赖
    2、当需要使用某个对象实例的时候, 就直接从容器中获取即可
    3、程序员可以更加关注如何使用对象完成相应的业务, (以前是new ... ==> 注解/配置
    方式)

Spring底层结构

DI 依赖注入

  • 可以理解成是IOC 的另外叫法.

Spring管理 IOC容器

基于XML配置Bean

通过类型获取Bean

ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
Monster monster = ioc.getBean(Monster.class);
  • 按类型来获取bean, 要求ioc 容器中的同一个类的bean 只能有一个, 否则会抛出异常
    NoUniqueBeanDefinitionException
  • 应用场景:比如XxxAction/Servlet/Controller, 或XxxService 在一个线程中只需要一个对象实例(单例)的情况
  • 在容器配置文件(比如beans.xml)中给属性赋值, 底层是通过
    setter 方法完成的, 这也是为什么我们需要提供setter 方法的原因

通过构造器获取Bean

按索引index

<bean id="monster02" class="com.spring.beans.Monster">
<constructor-arg value="2" index="0"/>
<constructor-arg value="蜘蛛精" index="1"/>
<constructor-arg value="吐口水" index="2"/>
</bean>

按类型type

<bean id="monster03" class="com.spring.beans.Monster">
<constructor-arg value="3" type="java.lang.Integer"/>
<constructor-arg value="白骨精" type="java.lang.String"/>
<constructor-arg value="白骨鞭" type="java.lang.String"/>
</bean>
  • 通过index 属性来区分是第几个参数
  1. 通过type 属性来区分是什么类型(按照顺序)

通过p名称空间配置Bean

<bean id="monster04" class="com.spring.beans.Monster"
p:monsterId="4"
p:name="红孩儿"
p:skill="吐火~"
/>

bean 对象的相互引用

  1. 其它含义和前面一样
  2. ref 表示memberDAO 这个属性将引用/指向id = memberDAOImpl 对象

引用/注入其它bean 对象

<bean id="memberServiceImpl" class="com.spring.service.MemberServiceImpl">
<property name="memberDAO" ref="memberDAOImpl"/>
</bean>
<bean id="memberDAOImpl" class="com.spring.dao.MemberDAOImpl"/>

引用/注入内部bean 对象

<bean id="memberServiceImpl02"
class="com.spring.service.MemberServiceImpl">
<property name="memberDAO">
<bean class="com.spring.dao.MemberDAOImpl"/>
</property>
</bean>

引用/注入集合/数组类型

  • 给集合属性注入值
<bean id="master01" class="com.code_study.spring.beans.Master">
<property name="name" value="太上老君"/>
<!-- 给bean 对象的list 集合赋值-->
<property name="monsterList">
<list>
<ref bean="monster03"/>
<ref bean="monster02"/>
</list>
</property>
  • 给bean 对象的map 集合赋值

    <property name="monsterMap">
    <map>
    <entry>
    <key>
    <value>monsterKey01</value>
    </key>
    <ref bean="monster01"/>
    </entry>
    <entry>
    <key>
    <value>monsterKey02</value>
    </key>
    <ref bean="monster02"/>
    </entry>
    </map>
    </property>
    
  • 给bean 对象的properties 集合赋值

    <property name="pros">
    <props>
    <prop key="k1">Java 工程师</prop>
    <prop key="k2">前端工程师</prop>
    <prop key="k3">大数据工程师</prop>
    </props>
    </property>
    
  • 给bean 对象的properties 集合赋值

    <property name="pros">
    <props>
    <prop key="k1">Java 工程师</prop>
    <prop key="k2">前端工程师</prop>
    <prop key="k3">大数据工程师</prop>
    </props>
    </property>
    
  • 给bean 对象的数组属性注入值

    <property name="monsterName">
    <array>
    <value>银角大王</value>
    <value>金角大王</value>
    </array>
    </property>
    
  • 给bean 对象的set 属性注入值

    <property name="monsterSet">
    <set>
    <ref bean="monster01"/>
    <bean class="com.spring.beans.Monster">
    <property name="monsterId" value="10"/>
    <property name="name" value="玉兔精"/>
    <property name="skill" value="钻地洞"/>
    </bean>
    </set>
    </property>
    </bean>
    

bean 配置信息重用(继承)

<bean id="monster10" class="com.hspedu.spring.beans.Monster">
<property name="monsterId" value="10"/>
<property name="name" value="蜈蚣精"/>
<property name="skill" value="蜇人"/>
</bean>

<!-- parent="monster10" 就是继承使用了monster10 的配置信息-->
<bean id="monster11" class="com.hspedu.spring.beans.Monster"
parent="monster10"/>

抽象类

<!-- 当我们把某个bean设置为abstract="true" 这个bean只能被继承,而不能实例化了-->
<bean id="monster12" class="com.hspedu.spring.beans.Monster" abstract="true">
<property name="monsterId" value="12"/>
<property name="name" value="美女蛇"/>
<property name="skill" value="吃人"/>
</bean>

bean 创建顺序

  • 在spring 的ioc 容器, 默认是按照配置的顺序创建bean 对象

  • 如果这样配置,会先创建department01 对象,再创建student01 对象.

    <bean id="student01" class="com.hspedu.bean.Student" depends-on="department01"/>
    <bean id="department01" class="com.hspedu.bean.Department" />
    

bean 对象的单例和多例

  • 在spring 的ioc 容器, 在默认是按照单例创建的,即配置一个bean 对象后,ioc 容器只会创建一个bean 实例。

  • 如果,我们希望ioc 容器配置的某个bean 对象,是以多个实例形式创建的则可以通过配置scope="prototype" 来指定

  1. 默认是单例singleton, 在启动容器时, 默认就会创建, 并放入到singletonObjects 集合

  2. 设置为多实例机制后, 该bean 是在getBean()时才创建

  3. 如果是单例singleton, 同时希望在getBean 时才创建, 可以指定懒加载lazy-init="true" (注意默认是false)

  4. 通常情况下, lazy-init 就使用默认值false , 在开发看来, 用空间换时间是值得的, 除非有特殊的要求.

  5. 如果scope="prototype" 这时你的lazy-init 属性的值不管是ture, 还是false 都是在getBean 时候,才创建对象.

bean 的生命周期

说明: bean 对象创建是由JVM 完成的,然后执行如下方法

  1. 执行构造器
  2. 执行set 相关方法
  3. 调用bean 的初始化的方法(需要配置)
  4. 使用bean
  5. 当容器关闭时候,调用bean 的销毁方法(需要配置)

细节:

  1. 初始化init 方法和destory 方法, 是程序员来指定
  2. 销毁方法就是当关闭容器时,才会被调用.

配置bean 的后置处理器

  1. 在spring 的ioc 容器,可以配置bean 的后置处理器
  2. 该处理器/对象会在bean 初始化方法调用前和初始化方法调用后被调用
  3. 程序员可以在后置处理器中编写自己的代码

1、怎么执行到这个方法?=> 使用AOP(反射+动态代理+IO+容器+注解)

2、有什么用?=> 可以对IOC 容器中所有的对象进行统一处理,比如日志处理/权限的校验/安全的验证/事务管理.

3、针对容器的所有对象吗? 是的=>切面编程特点

通过属性文件给bean 注入值

<context:property-placeholder location="classpath:my.properties"/>
<bean id="monster100" class="com.code_study.spring.beans.Monster">
<property name="monsterId" value="${id}"/>
<property name="name" value="${name}"/>
<property name="skill" value="${skill}"/>
</bean>

基于XML 的bean 的自动装配

<bean id="orderAction" autowire="byName"
class="com.code_study.spring.action.OrderAction"/>
<bean id="orderService" autowire="byName"
class="com.code_study.spring.service.OrderService"/>
<bean id="orderDao" class="com.code_study.spring.dao.OrderDao"/>

特别说明:

  • autowire = "byName" 会自动去找id 为setXxxx 后面Xxxx 的bean 自动组装.,如果找到就装配,如果找不到就报错,

  • 比如这里的

    就会去找OrderAction 类中定义的setOrderService 的id 为orderService 的OrderServicebean 组装,找到就组装,找不到就组装失败

基于注解配置Bean

基本介绍

基于注解的方式配置bean, 主要是项目开发中的组件,比如Controller、Service、和Dao.

组件注解的形式有

  1. @Component 表示当前注解标识的是一个组件
  2. @Controller 表示当前注解标识的是一个控制器,通常用于Servlet
  3. @Service 表示当前注解标识的是一个处理业务逻辑的类,通常用于Service 类
  4. @Repository 表示当前注解标识的是一个持久化层的类,通常用于Dao 类
  • !!!Spring 的IOC 容器不能检测一个使用了@Controller 注解的类到底是不是一个真正的控
    制器。注解的名称是用于程序员自己识别当前标识的是什么组件。其它的@Service
    @Repository 也是一样的道理[也就是说spring 的IOC 容器只要检查到注解就会生成对象,
    但是这个注解的含义spring 不会识别,注解是给程序员编程方便看的]!!!

自动扫描包

<!-- 配置自动扫描的包,注意需要加入context 名称空间-->
<context:component-scan base-package="com.code_study.spring.component" />

扫描时排除某些类 -expression

<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Service"/>

指定自动扫描哪些注解类

<!--
1. use-default-filters="false": 不再使用默认的过滤机制
2. context:include-filter: 表示只是扫描指定的注解的类
3.expression="org.springframework.stereotype.Controller": 注解的全类名
-->
<context:component-scan base-package="com.code_study.spring.component"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

修改beanName

标记注解后,类名首字母小写作为id 的值。也可以使用注解的value 属性
指定id 值,并且value 可以省略

AOP

动态代理

// 1.获取类加载对象
ClassLoader loader = target_obj.getClass().getClassLoader();
// 2.获取接口类型数组
Class<?>[] interfaces = target_obj.getClass().getInterfaces();
// 3.获取InvocationHandler 以匿名内部类的方式方式来获取InvocationHandler
InvocationHandler h = new InvocationHandler() {
// 4.以动态代理的方式调用目标对象的目标方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
String methodName = method.getName();
try {
// 1. 在调用目标方法之前打印“方法开始”日志
System.out.println("日志--方法名:" + methodName + "--方法开始--参数:"
+ Arrays.asList(args));
// 2. 调用目标方法并接收返回值
result = method.invoke(target_obj, args);
// 3. 在目标方法结束后打印“方法结束”日志
System.out.println("日志--方法名:" + methodName
+ "--方法正常结束--结果:result=" + result);
} catch (Exception e) {
// 4.如果目标方法抛出异常,打印“方法异常”日志
e.printStackTrace();
System.out.println("日志--方法名:" + methodName
+ "--方法抛出异常--异常类型:" + e.getClass().getName());
} finally {
// 5.在finally 中打印“方法最终结束”日志
System.out.println("日志--方法名:" + methodName + "--方法最终结
束");
}
// 6. 返回目标方法的返回值
return result;
}
};
//生成SmartAnimaleable 的代理对象
//需要三个参数,
//1.就是loader(获取类加载对象)
//2.接口数组
//3.InvocationHandler 对象[这个相对麻烦..]
SmartAnimalable proxy = (SmartAnimalable) Proxy.newProxyInstance(
loader, interfaces, h);
return proxy;
}
}

AOP 的基本介绍

AOP 的全称(aspect oriented programming) ,面向切面编程


在切面类中声明通知方法

  1. 前置通知:@Before
  2. 返回通知:@AfterReturning
  3. 异常通知:@AfterThrowing
  4. 后置通知:@After
  5. 环绕通知:@Around

细节

  • 关于切面类方法命名可以自己规范一下, 比如showBeginLog() . showSuccessEndLog()
    showExceptionLog() , showFinallyEndLog()

  • 切入表达式的更多配置,比如使用模糊配置
    @Before(value="execution(* com.code_study.aop.proxy.SmartDog.*(..))")

  • 表示所有访问权限,所有包的下所有有类的所方法,都会被执行该前置通知方法
    @Before(value="execution(* .(..))")

  • 当spring 容器开启了

  • <!-- 开启基于注解的AOP 功能--> <aop:aspectj-autoproxy/> 
    

    , 我们获取注入的对象, 需要以接口的类型来获取, 因为你注入的对象.getClass() 已经是代理类型了!

  • 当spring 容器开启了

    <!-- 开启基于注解的AOP 功能--> <aop:aspectj-autoproxy/> 
    

    , 我们获取注入的对象, 也可以通过id 来获取, 但是也要转成接口类型.

开启基于注解的AOP 功能

<aop:aspectj-autoproxy/>

AOP-切入表达式



  1. 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效
  2. 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效
  3. 切入表达式也可以对没有实现接口的类,进行切入

通过JoinPoint 可以获取到调用方法的签名

public void beforeMethod(JoinPoint joinPoint){
joinPoint.getSignature().getName(); // 获取目标方法名
joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属
类的简单类名
joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、
protected)
Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
joinPoint.getTarget(); // 获取被代理的对象
joinPoint.getThis(); // 获取代理对象自己
}

AOP-返回通知获取结果

添加属性 returning = "res"

@AfterReturning(value = "execution(public float
com.code_study.spring.aop.joinpoint.SmartDog.getSum(float, float))",
returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
System.out.println("返回通知" + "--结果是--" + res );
}

AOP-异常通知中获取异常

添加属性 throwing = "throwable"

@AfterThrowing(value = "execution(public float
com.code_study.spring.aop.joinpoint.SmartDog.getSum(float, float))",
throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
System.out.println("异常通知-- 异常信息--" + throwable);
}

AOP-切入点表达式重用

@Pointcut(value = "execution(public float
com.code_study.spring.aop.joinpoint.SmartDog.getSum(float, float))")
public void myPointCut() {
}
@Before(value = "myPointCut()")
public void showBeginLog(JoinPoint joinPoint) { //前置方法

AOP-切面优先级问题

如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制.?

使用:注解@order(value=n) 来控制n 值越小,优先级越高.

AOP输出的信息顺序

  1. 不能理解成:优先级高的每个消息通知都先执行,这个和方法调用机制(和Filter 过滤器链式调用类似)

AOP-基于XML 配置AOP

在spring 中,我们也可以通过xml 的方式来配置AOP

<!-- 配置SmartAnimalAspect bean -->
<bean id="smartAnimalAspect"
class="com.code_study.spring.aop.xml.SmartAnimalAspect"/>
<!--配置SmartDog-->
<bean class="com.code_study.spring.aop.xml.SmartDog" id="smartDog"/>
<aop:config>
<!-- 配置统一切入点-->
<aop:pointcut expression="execution(public float
com.code_study.spring.aop.xml.SmartDog.getSum(float, float))"
id="myPointCut"/>
<aop:aspect ref="smartAnimalAspect" order="1">
<!-- 配置各个通知对应的切入点-->
<aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
<aop:after-returning method="showSuccessEndLog"
pointcut-ref="myPointCut" returning="res"/>
<aop:after-throwing method="showExceptionLog"
pointcut-ref="myPointCut" throwing="throwable"/>
<aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
<!-- 还可以配置环绕通知-->
<!-- <aop:around method=""/> -->
</aop:aspect>
</aop:config>
</beans>

JdbcTemplate

JdbcTemplate-基本介绍

  1. 通过Spring 可以配置数据源,从而完成对数据表的操作

  2. JdbcTemplate 是Spring 提供的访问数据库的技术。可以将JDBC 的常用操作封装为模板方
    法。[JdbcTemplate 类图].

JdbcTemplate创建配置文件

引入外部属性文件

<!-- 引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

配置数据源

<!-- 配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.userName}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
</beans>

配置JdbcTemplate

<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 将上面的数据源分配给jdbcTemplate -->
<property name="dataSource" ref="dataSource"/>
</bean>

JdbcTemplate对数据库的CRUD操作

添加 execute&update

ApplicationContext ioc = new
ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
// 1. 添加方式1
// String sql = "INSERT INTO monster VALUES(400, '红孩儿', '枪法厉害')";
// bean.execute(sql);
//2. 添加方式2, 绑定参数
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
int affected = bean.update(sql, 700, "红孩儿2", "枪法厉害2");
System.out.println("add ok affected= " + affected);

修改 update

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
String sql = "UPDATE monster SET skill = ? WHERE id=?";
int affected = bean.update(sql, "美女计", 300);
System.out.println("affected= " + affected);
System.out.println("update data ok~");

批量添加 batchUpdate

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);//添加..
String sql = "INSERT INTO monster VALUES(?, ?, ?)";
List<Object[]> param_list = new ArrayList<Object[]>();
param_list.add(new Object[]{500, "白蛇精", "吃人"});
param_list.add(new Object[]{600, "青蛇精", "吃小孩"});
bean.batchUpdate(sql, param_list);
System.out.println("batch add ok");

查询并封装到Monster 实体对象

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
String sql = "SELECT id as monsterId,name,skill FROM monster WHERE id =?";
//下面这个rowmapper 是一个接口,可以将查询的结果,封装到你指定的Monster 对象中.
RowMapper<Monster> rowMapper =
new BeanPropertyRowMapper<Monster>(Monster.class);
Monster monster = bean.queryForObject(sql, rowMapper, 100);

查询并批量封装到Monster 实体对象

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
String sql = "SELECT id as monsterId,name,skill FROM monster WHERE id >=?";
//下面这个rowmapper 是一个接口,可以将查询的结果,封装到你指定的Monster 对象中.
RowMapper<Monster> rowMapper = new
BeanPropertyRowMapper<Monster>(Monster.class);
List<Monster> monster_list =
bean.query(sql, rowMapper, 200);
for (Monster monster : monster_list) {
System.out.println(monster);
}

查询单行单列

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到JdbcTemplate bean
JdbcTemplate bean = ioc.getBean(JdbcTemplate.class);
String sql = "SELECT name FROM monster WHERE id =100";
String name = bean.queryForObject(sql, String.class);
System.out.println(name);

使用Map 传入具名参数

1.配置NamedParameterJdbcTemplate,支持具名参数

<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<!-- 这里需要使用构造器关联数据源-->
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>

2.使用Map 传入具名参数完成操作

ApplicationContext ioc =
new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
String sql = "INSERT INTO monster VALUES(:my_id, :name, :skill)";
Map<String, Object> map_parameter = new HashMap<String, Object>();
map_parameter.put("my_id", 800);
map_parameter.put("name", "螃蟹精");
map_parameter.put("skill", "钳子无敌大法");
namedParameterJdbcTemplate.update(sql, map_parameter);

使用sqlparametersoruce 来封装具名参数

ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");
//得到NamedParameterJdbcTemplate bean
NamedParameterJdbcTemplate namedParameterJdbcTemplate =
ioc.getBean(NamedParameterJdbcTemplate.class);
String sql = "INSERT INTO monster VALUES(:monsterId, :name, :skill)";
Monster monster = new Monster(900, "狐狸精", "狐媚之术");
SqlParameterSource source = new BeanPropertySqlParameterSource(monster);
namedParameterJdbcTemplate.update(sql, source);
System.out.println("add ok~");

声明式事务

  • 使用Spring 的声明式事务处理, 可以将一个事务的多个子步骤(sql语句)分别写成一个方法,然后统一管理.
  • 这个是Spring 很牛的地方,在开发使用的很多,优点是无代码冗余,效率高,扩展方便
  • 底层使用AOP (动态代理+动态绑定+反射+注解)

配置声明式事务

引入外部属性文件

<context:property-placeholder location="classpath:jdbc.properties"/>

配置数据源

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.userName}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>

配置JdbcTemplate

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 将上面的数据源分配给jdbcTemplate -->
<property name="dataSource" ref="dataSource"/>
</bean>

配置事务管理器

<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

开启基于注解的声明式事务功能

<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

事务的传播机制

事务传播机制种类

事务传播的属性/种类机制分析,重点 REQUIRED 和REQUIRED_NEW 两种事务

事务的传播机制的设置方法

REQUIRES_NEW 和REQUIRED 在处理事务的策略

  1. 如果设置为REQUIRES_NEW
    方法2 如果错误,不会影响到Tx1反之亦然,即它们的事务是独立的.
  2. 如果设置为REQUIRED 方法2和Tx1是一个整体,只要有方法的事务错误,那么两个方法都不会执行成功.!

事务的隔离级别

  1. 默认的隔离级别, 就是mysql 数据库默认的隔离级别一般为REPEATABLE_READ

  2. 看源码可知Isolation.DEFAULT 是:Use the default isolation level of the underlying
    datastore

  3. 查看数据库默认的隔离级别SELECT @@global.tx_isolation

事务的超时回滚

  1. 如果一个事务执行的时间超过某个时间限制,就让该事务回滚。

  2. 可以通过设置事务超时回顾来实现

  3. 使用注解 @Transactional(timeout = 2)

​ 超时时间,设置为2 秒)

手写Spring底层机制

IOC容器

    //定义 BeanDefinitionMap 存放 beanDefinition
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<>();

    //定义 singletonObjects 存放 单例
    private ConcurrentHashMap<String,Object> singletonObjects =
            new ConcurrentHashMap<>();


    //定义beanPostProcessorList 存放 BeanPostProcessor
    private ArrayList<BeanPostProcessor> beanPostProcessorList=
            new ArrayList<>();


构造器

//构造器
public ZyApplicationContext(Class configClass) {
    this.configClass = configClass;

    beanDefinitionsByScan();

    //初始化单例池
    initSingletonObjects();
}

扫描包

private void beanDefinitionsByScan() {
        //获得扫描的包
        ComponentScan componentScan =
                (ComponentScan)this.configClass.getDeclaredAnnotation(ComponentScan.class);

        //获取路径
        String path = componentScan.value();

        path = path.replace(".","/");
        //获取工作路径
        ClassLoader classLoader = ZyApplicationContext.class.getClassLoader();

        URL resource = classLoader.getResource(path);

        File file = new File(resource.getFile());
        if (file.isDirectory()){
            File[] files = file.listFiles();
            for (File f : files) {
                //获取绝对路径
                String fileAbsolutePath = f.getAbsolutePath();
                if (fileAbsolutePath.endsWith(".class")) {
                    //获取className
                    String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));

                    String fullPath = path.replace("/", ".") + "." + className;

                    try {
                        Class<?> clazz = classLoader.loadClass(fullPath);
                        if (clazz.isAnnotationPresent(Component.class)) {

                            //初始化beanPostProcessorList
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                              BeanPostProcessor beanPostProcessor =
                                      (BeanPostProcessor)clazz.newInstance();
                                beanPostProcessorList.add(beanPostProcessor);
                                continue;
                            }


                            //处理className
                            String value = clazz.getDeclaredAnnotation(Component.class).value();
                            if ("".equals(value)){
                                className = StringUtils.uncapitalize(className);
                            }else {
                                className = value;
                            }
                            System.out.println("是一个bean 类名= " + className);
                            //设置 beanDefinition
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //设置scope
                            if (clazz.isAnnotationPresent(Scope.class)){
                                beanDefinition.setScope(clazz.getDeclaredAnnotation(Scope.class).value());
                            }else{
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinition.setClazz(clazz);

                            //放入 beanDefinitionMap
                            beanDefinitionMap.put(className,beanDefinition);




                        } else {
                            System.out.println("不是一个bean 类名= " + className);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

初始化单例池

private void initSingletonObjects() {
        Enumeration<String> keys = beanDefinitionMap.keys();
        while (keys.hasMoreElements()){
            String beanName = keys.nextElement();
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)){
                Object bean = createBean(beanDefinition,beanName);
                singletonObjects.put(beanName,bean);
            }

        }
    }

getBean()

 public Object getBean(String name){
        if (beanDefinitionMap.containsKey(name)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)){
                return singletonObjects.get(name);
            }else{
                return createBean(beanDefinition,name);
            }
        }
        return null;
    }

createBean()

 private Object createBean(BeanDefinition beanDefinition,String beanName){
        try {
            Class clazz = beanDefinition.getClazz();
            Object instance = clazz.getDeclaredConstructor().newInstance();
            
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

依赖注入

  • 加入到createBean()中

  private Object createBean(BeanDefinition beanDefinition,String beanName){
        try {
            Class clazz = beanDefinition.getClazz();
            Object instance = clazz.getDeclaredConstructor().newInstance();

            //依赖注入
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    if (declaredField.getDeclaredAnnotation(Autowired.class).required()){
                        //获的字段名
                        String fieldName = declaredField.getName();
                        //获取实例
                        Object bean = getBean(fieldName);
                        declaredField.setAccessible(true);
                        declaredField.set(instance,bean);
                    }
                }
            }

          
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

后置处理器

  • 加入到createBean()中

  private Object createBean(BeanDefinition beanDefinition,String beanName){
        try {
            Class clazz = beanDefinition.getClazz();
            Object instance = clazz.getDeclaredConstructor().newInstance();

            //依赖注入
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    if (declaredField.getDeclaredAnnotation(Autowired.class).required()){
                        //获的字段名
                        String fieldName = declaredField.getName();
                        //获取实例
                        Object bean = getBean(fieldName);
                        declaredField.setAccessible(true);
                        declaredField.set(instance,bean);
                    }
                }
            }

            //后置处理器 before()
            //遍历 beanPostProcessorList
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.
                        postProcessBeforeInitialization(instance, beanName);
                if (current!=null){
                    instance = current;
                }

            }


            //初始化bean
            if (instance instanceof InitializingBean){
                try {
                    ((InitializingBean)instance).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }



            //后置处理器 after()
            //遍历 beanPostProcessorList
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.
                        postProcessAfterInitialization(instance, beanName);
                if (current!=null){
                    instance = current;
                }

            }


            System.out.println("");
            System.out.println("-------------------------------------");
            System.out.println("");
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

AOP

  • AOP需要在后置处理器的before方法中实现

 @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("ZyBeanPostProcessor后置处理器-After-beanName= "+beanName);


        //aop实现

        if("smartDog".equals(beanName)) {
            Object proxyInstance = Proxy.newProxyInstance(ZyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {


                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result = null;

                    if ("getSum".equals(method.getName())) {
                        SmartAnimalAspect.showBeginLog();
                        result = method.invoke(bean, args);
                        SmartAnimalAspect.showBeginLog();
                    }else {
                        result = method.invoke(bean, args);//执行目标方法
                    }

                    return result;
                }
            });
            return proxyInstance;
        }

        return bean;
    }

几个Spring的问题

1.单例/多例怎么实现的?@scope为什么可以实现?

回答:@scope 的value属性可以设置为singleton /prototype

通过getBean()方法 如果bean中的属性scope为singleton 就从单例池直接拿,如果是prototype 就调用createBean()创建一个实例

2.如何实现依赖注入?@Autowired Spring容器如何实现依赖注入?

回答: 遍历clazz的所有属性 通过@Autowired注解 如果有 先获取字段名 再通过getBean()获取对应的bean 最后用filed.set()方法将实例的该属性设置为 获取到的bean 实现依赖注入

3.后置处理器 为什么在创建bean 时 调用bean 的 init方法初始化前/后 调用?

//后置处理器 before()
            //遍历 beanPostProcessorList
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.
                        postProcessBeforeInitialization(instance, beanName);
                if (current!=null){
                    instance = current;
                }

            }


            //初始化bean
            if (instance instanceof InitializingBean){
                try {
                    ((InitializingBean)instance).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }



            //后置处理器 after()
            //遍历 beanPostProcessorList
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                Object current = beanPostProcessor.
                        postProcessAfterInitialization(instance, beanName);
                if (current!=null){
                    instance = current;
                }

            }

4.后置处理器和AOP有什么关系,Spring Aop如何实现??

回答:aop的实现实在后置处理器的before中实现的,底层使用动态代理

 //aop实现

        if("smartDog".equals(beanName)) {
            Object proxyInstance = Proxy.newProxyInstance(ZyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {


                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result = null;

                    if ("getSum".equals(method.getName())) {
                        SmartAnimalAspect.showBeginLog();
                        result = method.invoke(bean, args);
                        SmartAnimalAspect.showBeginLog();
                    }else {
                        result = method.invoke(bean, args);//执行目标方法
                    }

                    return result;
                }
            });
            return proxyInstance;
        }

        return bean;

本文学习内容来自韩顺平老师的课程

仅供个人参考学习

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年04月09日 0

暂无评论

推荐阅读
5DfGM4DuibK0