Spring5学习详细笔记
- Spring学习总结 — 工厂
- Spring学习总结 — 工厂高级特性
- Spring学习总结 — AOP编程
- Spring学习总结 — 持久层整合
- Spring学习总结 — 注解编程
Spring学习总结 — 工厂
第一章 :引言
1. 什么是spring
spring是一个轻量级的JavaEE解决方案,整合了众多优秀的设计模式
-
轻量级
1.对于运行环境是没有要求的 tomcat resion jetty 2.代码移植性高 不需要实现额外的接口 -
JaveEE的解决方案

-
整合代理模式
1.工厂 2.代理 3.模板 4.策略
2. 设计模式
1. 广义概念
面对对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4人帮定义的23中设计模式:工厂,适配器,门面,代理...
3. 工厂设计模式
3.1 什么是工厂设计模式
1. 概念:通过⼯⼚类,创建对象
User user = new User();
UserDAO userDAO = newUserDAOImpl();
2. 1. 好处:解耦合
2. 耦合:指定是代码间的强关联关系,⼀⽅的改变会影响到另⼀⽅
3. 问题:不利于代码维护
4. 简单:把接⼝的实现类,硬编码在程序中
UserService userService =new UserServiceImpl();
3.2 简单工厂的设计
package com.it.factory;
import com.it.service.UserService;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @Author muss
* @Date 2021/4/11 15:19
*/
public class BeanFactory {
private static final Properties properties =new Properties();
static {
try {
InputStream is = BeanFactory.class.getResourceAsStream("/application.properties");
properties.load(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService() {
UserService userService = null;
try {
Class<?> aClass = Class.forName(properties.getProperty("userService"));
userService = (UserService) aClass.newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
return userService;
}
}
3.3 通用工厂的设计
/**
*
* @param key 传入需要的对象对应的key
* @return 返回需要的对象
*/
public static Object getBean(String key){
Object object = null;
try{
object = Class.forName(key).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return object;
}
3.4 通用工厂的使用方法
1. 定义类型(类)
2. 通过配置文件的配置告知工厂(即application.properties)
key = value
3. 通过工厂获取了类的对象
Object object = BeanFactory.getBean(key);
4. 总结
Spring本质:工厂 ApplicationContext(applicationContext.xml)
第二章 :第一个的Spring程序
1. 软件版本
1. jDK1.8+
2. Maven 3.5+
3. IDEA 2020.3
4. SpringFramework 5.1.4
2. 环境搭建
-
Spring的jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.4.RELEASE</version> </dependency> -
Spring的配置文件
1. 配置文件的放置位置:任意位置 没有硬性要求 2. 配置文件的命名:任意名字 推荐使用:applicationContext.xml
3. Spring的核心API
-
ApplicationContext
1. 作用:Spring提供ApplicationContext这个工厂是用于进行对象的创建 2. 好处:解耦合-
ApplicationContext 接口类型
使用接口 :屏蔽实现的差异 非web环境下使用:ClassPathXmlApplicationContext (可以在main方法中,junit中使用) web环境下使用:XmlWebApplicationContext[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2NxJCypr-1618670320032)(Spring5.assets/image-20210411160632964.png)]
-
重量级资源
1. ApplicationContext工厂的对象会占用大量的内存 2. 不会频繁的创建对象: 一般一个应用只会创建一个对象 3. ApplicaitonContext工厂:是 线程安全 的,所以可以多线程访问
-
4. 程序开发
1. 创建类型
2. 在配置文件applicationContext.xml, 中配置
<bean id="user" class="com.muss.bean.User"/>
3. 通过工厂类ApplicationContext,获取对象
ApplicationContext
|- ClassPathXmlApplicationContext
ApplicationContext ac = new ClassPathXmlApplicationContext(“/applicationContext.xml”)
5.细节分析
-
名词解释
Spring工厂创建的对象,叫做bean或者组件(Componet) -
Spring工厂的相关方法
@Test public void test01() { ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml"); // 第一种获取bean的方法 User user = (User) ac.getBean("user"); // 第二种获取bean的方法 User user1 = ac.getBean("user", User.class); // 第三种获取bean的方法 !这种方法只能在配置文件中只配置了一个改类型的bean时生效,否则报错 User bean = ac.getBean(User.class); // 获取Spring工厂配置文件下所有bean标签的id值 String[] beanDefinitionNames = ac.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println("beanDefinitionName= " + beanDefinitionName); } // 根据类型获取bean的id值 String[] beanNamesForType = ac.getBeanNamesForType(User.class); for (String s : beanNamesForType) { System.out.println("s= " + s); } // 用于判断是否存在有指定id值的bean,name属性的值不行 boolean user2 = ac.containsBeanDefinition("user"); System.out.println("boolean= " + user2); // 用于判断是否存在指定id的单例bean,name属性的值同样可以 boolean user3 = ac.containsBean("user"); System.out.println("boolean= " + user3); } -
配置文件
1. bean标签中只配置class属性 <bean class="com.muss.bean.User"/> 1:如果没有id值的话,使用工厂对象的getBeanNamesForType方法时,返回的id值为系统默认的值,如com.muss.bean.User#01 2:这种配置方式的应用场景:如果这个bean只需要使用一次,那么就可以省略id值 如果这个bean会使用多次,或被其他bean引用的话则需要设置id值 2. bean标签中的name属性 作用:用在Spring的配置文件中,为bean对象定义别名(小名) 与id属性的异同: 相同:都可以使用工厂对象的getbean方法 ac.getBean("user") == ac.getBean("u1") 区别:别名可以定义多个且都有效,但id属性可以定义多个,但是只有第一个有效,其他的值会报错
6. Spring工厂的底层实现原理(简易版)

- 首先通过ClassPathXmlApplicationContext工厂读取配置文件 application.xml
- 获取配置文件中bean标签的相关信息 id值,class值
- 然后通过反射创建对象,底层会调用对象自己的构造方法 !Spring工厂不管是公共的还是私有的都可以调用
7. 思考
问题:是否在开发中所有的对象都会交给Spring工厂来创建?
回答:理论上是,但实际上实体对象(entity)是不会交给Spring来创建的,而是有持久层框架(mybatis)来创建
第三章 :Spring5与日志框架的整合
Spring与日志框架整合,日志框架就可以在控制台中,输出Spring框架运行过程中一些重要的信息
好处: 便于了解Spring框架运行过程,利于程序的调试
-
Spring整合日志框架
-
导入maven坐标
<!-- 日志框架所需的jar包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> -
引入配置文件
# 配置根 log4j.rootLogger=debug,console # 日志输出到控制台显示 log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Target=System.out log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern= %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
-
第四章 :注入(Injection)
1. 什么是注入
通过Spring工厂即配置文件,为所创建的对象的成员变量赋值
1.1 为什么需要注入
通过编码的方式,为成员变量赋值,存在耦合

1.2 如何进行注入
-
类的成员变量提供了setget方法
-
配置Spring的配置文件
<bean id="account" class="com.it.bean.Account"> <property name="id"> <value> 11 </value> </property> <property name="name"> <value> xiaojr </value> </property> </bean>
1.3 注入的好处
解耦合
2. Spring注入的原理分析(简易版)
Spring通过底层调用对象属性对应的set方法,完成对变量的赋值,这种方法也称为set注入
第五章 :Set注入详解
针对于不同的类型的成员变量,在<property>标签中,需要嵌套其他标签
1. JDK类型
1.1 JDK八大基本类型+String类型
<value>qiqi</value>
1.2 数组
<list>
<value>haha01@qq.com</value>
<value>haha02@qq.com</value>
<value>haha03@qq.com</value>
</list>
1.3 Set集合
<property name="tels">
<set>
<value>haha</value>
<list>
<value>aa</value>
<value>bb</value>
</list>
</set>
</property>
1.4 List集合
<list>
<value>aaa</value>
<set>
<value>01</value>
<value>02</value>
</set>
</list>
1.5 Map集合
注意:map --> entry --> key有特定标签<key></key>,值需要根据对应类型来选择标签
<map>
<entry>
<key>
<value>aaa</value>
</key>
<value>hhh</value>
</entry>
</map>
1.6 Properties类型
Porperties类型 是特殊的 Map类型 key=String,value=String
<props>
<prop key="a">aa</prop>
<prop key="b">bb</prop>
</props>
2. 用户自定义类型
2.1 第一种方法
-
为成员变量提供getset方法
-
配置文件中进行注入
<bean id="userService" class="xxxx.UserServiceImpl"> <property name="userDAO"> <bean class="xxx.UserDAOImpl"/> </property> </bean>
2.2 第二种方法
-
第⼀种赋值⽅式存在的问题
1. 配置⽂件代码冗余 2. 被注⼊的对象(UserDAO),多次创建,浪费(JVM)内存资源 -
为成员变量提供set get⽅法
-
配置⽂件中进⾏配置
<bean id="userDAO" class="xxx.UserDAOImpl"/> <bean id="userService" class="xxx.UserServiceImpl"> <property name="userDAO"> <ref bean="userDAO"/> </property> </bean> #Spring4.x 废除了 <ref local=""/> 基本等效 <ref bean=""/>
3. Set注入的简写方法
3.1 基于属性简化
JDK类型注⼊
<property name="name" value="suns"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签
⽤户⾃定义类型
<property name="userDAO" ref="userDAO"/>
3.2 基于p命名空间简化
JDK类型注⼊
<bean id="person"class="xxx.Person" p:name="suns"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签
⽤户⾃定义类型
<bean id="userService" class="xxx.UserServiceImpl" p:userDAO-ref="userDAO"/>
第六章: 构造注入
注⼊:通过Spring的配置⽂件,为成员变量赋值
Set注⼊:Spring调⽤Set⽅法 通过配置⽂件 为成员变量赋值
构造注⼊:Spring调⽤构造⽅法 通过配置⽂件 为成员变量赋值
1. 开发步骤
-
提供有参构造⽅法
-
Spring的配置⽂件
<bean id="customer" class="com.baizhiedu.basic.constructer.Customer"> <constructor-arg> <value>suns</value> </constructor-arg> <constructor-arg> <value>102</value> </constructor-arg> </bean>
2. 构造⽅法重载
2.1 参数个数不同时
通过控制<constructor-arg>标签的数量进⾏区分
2.2 构造参数个数相同时
通过在标签引⼊ type属性 进⾏类型的区分<constructor-arg type="">
3. 注⼊的总结
未来的实战中,应⽤set注⼊还是构造注⼊?
答案:set注⼊更多
1. 构造注⼊麻烦 (重载)
2. Spring框架底层 ⼤量应⽤了set注⼊

第七章:反转控制 与 依赖注⼊
1. 反转控制(IOC Inverse of Control)
控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring⼯⼚和配置⽂件中完成
好处:解耦合
底层实现:⼯⼚设计模式

2. 依赖注⼊ (Dependency InjectionDI)
注⼊:通过Spring的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值
依赖注⼊:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本类的成员变量,最终通过Spring配置⽂件进⾏注⼊(赋值)
好处:解耦合

第八章:Spring⼯⼚创建复杂对象

1. 什么是复杂对象
复杂对象:指的就是不能直接通过new构造⽅法创建的对象
例如:
Connection
SqlSessionFactory
2. Spring⼯⼚创建复杂对象的3种⽅式
2.1 FactoryBean接⼝
-
开发步骤
- 实现FactoryBean接口

-
Spring配置⽂件的配置
# 如果Class中指定的类型 是 FactoryBean接⼝的实现类,那么通过id值获得的是这个类所创建的复杂对象 Connection <bean id="conn"class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
-
细节
-
如果就想获得FactoryBean类型的对象ctx.getBean("&conn") 获得就是ConnectionFactoryBean对象
-
isSingleton⽅法 :返回 true 只会创建⼀个复杂对象, 返回 false 每⼀次都会创建新的对象
问题:根据这个对象的特点 ,决定是返回true(SqlSessionFactory) 还是 false (Connection)
-
mysql⾼版本连接创建时,需要制定SSL证书。
解决问题的⽅式: url ="jdbc:mysql://localhost:3306/suns?useSSL=false" -
依赖注⼊的体会(DI)
把ConnectionFactoryBean中依赖的4个字符串信息 ,进⾏配置⽂件的注⼊ 好处:解耦合 <bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
-
-
FactoryBean的实现原理[简易版]
使用了接⼝回调 1. 为什么Spring规定FactoryBean接⼝实现 并且 getObject() ? 2. ctx.getBean("conn") 获得是复杂对象 Connection ⽽没有 获得ConnectionFactoryBean(&) ?- 通过conn获得ConnectionFactoryBean类的对象 ,进⽽通过instanceof 判断出是FactoryBean接⼝的实现类
- Spring按照规定 getObject() —> Connection
- 返回Connection

-
FactoryBean总结
Spring中⽤于创建复杂对象的⼀种⽅式,也是Spring原⽣提供的,后续讲解Spring整合其他框架,⼤量应⽤FactoryBean
2.2 实例⼯⼚
1. 避免Spring框架的侵⼊
2. 整合遗留系统
-
开发步骤
<bean id="connFactory" class="com.baizhiedu.factorybean.ConnectionFactory"></bean> <bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>
2.3 静态⼯⼚
-
开发步骤
<bean id="conn" class="com.baizhiedu.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
3. Spring⼯⼚创建对象的总结

第九章:控制Spring⼯⼚创建对象的次数
1. 如何控制简单对象的创建次数
<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>
sigleton:只会创建⼀次简单对象 默认值
prototype:每⼀次都会创建新的对象
2.如何控制复杂对象的创建次数
FactoryBean{
isSingleton(){
return true 只会创建⼀次
return false 每⼀次都会创建新的
}
}
//如没有isSingleton⽅法 还是通过scope属 性 进⾏对象创建次数的控制
3. 为什么要控制对象的创建次数?
好处:节省不必要的内存浪费
-
什么样的对象创建一次
1. SqlSessionFactory 2. DAO 3. Service 例如:线程安全的,可以被多次使用的 -
什么样的对象创建多次
1. Connection 2. SqlSession | Session 3. Struts2 Action 例如:线程不安全的,不可以多次使用
Spring学习总结 — 工厂高级特性
第一章:对象的生命周期
1. 什么是对象的生命周期
指的是一个对象的创建、存活、销毁的一个完整过程
2. 为什么要学习对象的生命周期
由Spring负责对象的创建、存活、销毁,了解生命周期,有利于更好使用Spring为我们创建的对象
3. 生命周期的三个阶段
-
创建阶段
Spring工厂何时创建对象-
scope=“singleton”
Spring工厂创建的同时,把对象创建 注意:设置scope=singleton 这种情况下想要在获取对象的同时,创建对象,便可以在配置文件中设置lazy-init属性 <bean lazy-init="true"/> -
scope=“prototype”
Spring⼯⼚会在获取对象的同时,创建对象 ac.getBean(“xxx”)
-
-
初始化阶段
Spring⼯⼚在创建完对象后,调⽤对象的初始化⽅法,完成对应的初始化操作 1. 初始化⽅法提供:程序员根据需求,提供初始化⽅法,最终完成初始化操作 2. 初始化⽅法调⽤:Spring⼯⼚进⾏调⽤可以通过两种方法来实现初始化方法
-
InitializingBean接口
//通过实现InitializingBean接口,在afterPropertiesSet方法设置初始化的操作 public void afterPropertiesSet(){} -
对象中提供⼀个普通的⽅法
//编写一个方法,然后在配置文件中设置 public void myInit(){} <bean id="product" class="xxx.Product" init-method="myInit"/> -
细节分析
-
如果⼀个对象即实现InitializingBean 同时⼜提供的 普通的初始化⽅法 顺序
先执行InitializingBean中的afterPropertiesSet方法,然后执行普通初始化方法 1. InitializingBean 2. 普通初始化⽅法 -
注⼊⼀定发⽣在初始化操作的前⾯
-
什么叫做初始化操作
资源的初始化:数据库 IO ⽹络...
-
-
-
销毁阶段
Spring销毁对象前,会调⽤对象的销毁⽅法,完成销毁操作 1. Spring什么时候销毁所创建的对象? ctx.close(); 2. 销毁⽅法:程序员根据⾃⼰的需求,定义 销毁⽅法,完成销毁操作 调⽤:Spring⼯⼚完成调⽤ 可以通过两种方法来实现销毁方法
-
DisposableBean
public void destroy() throws Exception{} -
定义⼀个普通的销毁⽅法
public void myDestroy() throws Exception{} <bean id="" class="" init-method="" destroymethod="myDestroy"/> -
细节分析
-
销毁⽅法的操作只适⽤于 scope=“singleton”
-
什么叫做销毁操作
主要指的就是 资源的释放操作 io.close() connection.close();
-
-
第二章:配置文件参数化
把Spring配置⽂件中需要经常修改的字符串信息,转移到⼀个更⼩的配置⽂件中
配置⽂件参数化:利于Spring配置⽂件的维护(修改)
1. 配置文件参数的开发步骤
-
提供一个小的配置文件
#需要以.properties结尾,对于所在位置,文件名称没有要求 jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/suns?useSSL=false jdbc.username = root jdbc.password = 123456 -
Spring的配置⽂件与⼩配置⽂件进⾏整合
applicationContext.xml <context:property-placeholderlocation = "classpath:/db.properties"/> -
在Spring配置⽂件中通过${key}获取⼩配置⽂件中对应的值

第三章:自定义类型转换器
1. 类型转换器
作⽤:Spring通过类型转换器把配置⽂件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进⽽完成了注⼊

2. ⾃定义类型转换器
原因:当Spring内部没有提供特定类型转换器时,⽽程序员在应⽤的过程中还需要使⽤,那么就需要程序员⾃⼰定义类型转换器
-
需要类 implement Converter接口
public class MyDateConverter implements Converter<String,Date> { /*convert⽅法作⽤: String ---> Date SimpleDateFormat sdf = new SimpleDateFormat(); sdf.parset(String) ---> Dateparam:source 代表的是配置⽂件中 ⽇期字符串 <value>2020-10-11</value> return : 当把转换好的Date作 为convert⽅法的返回值后,Spring⾃动的为birthday属性进⾏注⼊(赋值) */ public Date convert(String source){ Date date = null; try{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); date = sdf.parse(source); }catch(ParseException e){ e.printStackTrace(); } return date; } -
在Spring的配置⽂件中进⾏配置
-
MyDateConverter对象创建出来
<bean id="myDateConverter" class="xxxx.MyDateConverter"/> -
类型转换器的注册
⽬的:告知Spring框架,我们所创建的MyDateConverter是⼀个类型转换器 <!--⽤于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean>
-
3. 细节
-
MyDateConverter中的⽇期的格式,通过依赖注⼊的⽅式,由配置⽂件完成赋值
<!--Spring创建MyDateConverter类型对象--> <bean id="myDateConverter" class="com.baizhiedu.converter.MyDateConverter"> <property name="pattern"value="yyyy-MM-dd"/> </bean> -
ConversionSeviceFactoryBean 定义 id属性 值必须conversionService
-
Spring框架内置⽇期类型的转换器
⽇期格式:2020/05/01
第四章:后置处理Bean
BeanPostProcessor作⽤:对Spring⼯⼚所创建的对象,进⾏再加⼯
- 后置处理Bean的运⾏原理分析

程序员实现BeanPostProcessor规定接⼝中的⽅法:
Object postProcessBeforeInitiallization(Object bean String beanName)
作⽤:Spring创建完对象,并进⾏注⼊后,可以运⾏Before⽅法进⾏加⼯获得Spring创建好的对象 :通过⽅法的参数最终通过返回值交给Spring框架
Object postProcessAfterInitiallization(Object bean String beanName)
作⽤:Spring执⾏完对象的初始化操作后,可以运⾏After⽅法进⾏加⼯获得Spring创建好的对象 :通过⽅法的参数最终通过返回值交给Spring框架
实战中:很少处理Spring的初始化操作:没有必要区分Before After。只需要实现其中的⼀个After⽅法即可
-
BeanPostProcessor的开发步骤
-
类 实现 BeanPostProcessor接⼝
public class MyBeanPostProcessor implements BeanPostProcessor{ @Override public Object postProcessBeforeInitialization(Object bean, StringbeanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, StringbeanName) throws BeansException { Categroy categroy = (Categroy) bean; categroy.setName("xiaowb"); return categroy; } } -
Spring的配置⽂件中进⾏配置
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/> -
BeanPostProcessor细节
BeanPostProcessor会对Spring⼯⼚中所有创建的对象进⾏加⼯。
-
Spring学习总结 — AOP编程
第一章:静态代理设计模式
1. 为什么需要代理设计模式
-
问题
-
Service层中包含了哪些代码?
Service层中 = 核⼼功能(⼏⼗⾏ 上百代码) + 额外功能(附加功能) 1. 核心功能 1. 业务运算 2. DAO调用 2. 额外功能 1. 不属于业务 2. 可有可无 3. 代码量很小 例如:事务、⽇志、性能... -
额外功能书写在Service层中好不好?
Service层的调⽤者的⻆度 (Controller):需要在Service层书写额外功能。 软件设计者的角度 Service层不需要额外功能 -
现实⽣活中的解决⽅式
-

2. 代理设计模式
1. 概念
通过代理类,为原始类(⽬标)增加额外的功能
好处:利于原始类(⽬标)的维护
2. 名词解释
1. ⽬标类 原始类
指的是 业务类 (核⼼功能 --> 业务运算DAO调⽤)
2. ⽬标⽅法,原始⽅法
⽬标类(原始类)中的⽅法 就是⽬标⽅法(原始⽅法)
3. 额外功能 (附加功能)
⽇志,事务,性能
3. 代理开发的核⼼要素
代理类 = ⽬标类(原始类) + 额外功能 +原始类(⽬标类)实现相同的接⼝
房东 ---> public interfaceUserService{
m1
m2
}
UserServiceImpl implements UserService{
m1 ---> 业务运算 DAO
调⽤m2
}
UserServiceProxy implements UserService
m1
m2
4. 编码
静态代理:为每⼀个原始类,⼿⼯编写⼀个代理类(.java .class)

5. 静态代理存在的问题
1. 静态类⽂件数量过多,不利于项⽬管理
UserServiceImpl
UserServiceProxy
OrderServiceImpl
OrderServiceProxy
2. 额外功能维护性差
代理类中 额外功能修改复杂(麻烦)
第二章:Spring的动态代理开发
1. Spring动态代理的概念
概念:通过代理类为原始类(⽬标类)增加额外功能
好处:利于原始类(⽬标类)的维护
2. 搭建开发环境
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springaop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
3. Spring动态代理的开发步骤
-
创建原始对象(⽬标对象)
public class PersonService { public void register(){ System.out.println("PersonService.register"); } } //在配置文件中 <bean id="personService" class="com.muss.serivice.PersonService"/> -
额外功能 MethodBeforeAdvice接⼝
//额外的功能书写在接⼝的实现中,运⾏在原始⽅法执⾏之前运⾏额外功能 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("-------方法执行之前我执行--------"); } //在配置文件中 <bean id="before" class="com.muss.dynamic.Before"/> -
定义切⼊点
切⼊点:额外功能加⼊的位置 ⽬的:由程序员根据⾃⼰的需要,决定额外功能加⼊给那个原始⽅法 register <aop:config> <aop:pointcut id="pc" expression="execution(* *(..))" /> </aop:config> -
组装 (2 3整合)
表达的含义:所有的⽅法 都加⼊before的额外功能 <aop:advisor advice-ref="before" pointcut-ref="pc"/> -
调⽤
⽬的:获得Spring⼯⼚创建的动态代理对象,并进⾏调⽤ ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); 注意: 1. Spring的⼯⼚通过原始对象的id值获得的是代理对象 2. 获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储 @Test public void test01() { ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml"); PersonService personService = ac.getBean("personService", PersonService.class); personService.register(); }
4. 动态代理细节分析
-
Spring创建的动态代理类在哪⾥?
Spring框架在运⾏时,通过动态字节码技术,在JVM创建的,运⾏在JVM内部,等程序结束后,会和JVM⼀起消失 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进⽽创建对象,当虚拟机结束,动态字节码跟着消失。 结论:动态代理不需要定义类⽂件,都是JVM运⾏过程中动态创建的,所以不会造成静态代理,类⽂件数量过多,影响项⽬管理的问题。

-
动态代理编程简化代理的开发
在额外功能不改变的前提下,创建其他⽬标类(原始类)的代理对象时,只需要指定原始(⽬标)对象即可。 -
动态代理额外功能的维护性⼤⼤增强
第三章:Spring动态代理详解
1. 额外功能的详解
-
MethodBeforeAdvice分析
1. MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作 public class Before implements MethodBeforeAdvice { /** * 作用:把运行在原始方法之前运行的额外功能,书写在before方法中 * @param method 原始方法 * @param args 原始方法的参数列表 * @param target 对象 * @throws Throwable 抛出异常 */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("-------方法执行之前我执行--------"); } } 2. before⽅法的3个参数在实战中,该如何使⽤。 before⽅法的参数,在实战中,会根据需要进⾏使⽤,不⼀定都会⽤到,也有可能都不⽤。 -
MethodInterceptor(⽅法拦截器)
methodinterceptor接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、前后 public class Around implements MethodInterceptor { /** * * @param invocation 原始方法 * @return 返回原始方法的返回值 * @throws Throwable 抛出异常 */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("--------方法执行之气那------"); Object ret = invocation.proceed(); System.out.println("--------方法执行之后-------"); return ret; } } //什么样的额外功能 运⾏在原始⽅法执⾏之前,之后都要添加? 例如事务 //额外功能运⾏在原始⽅法抛出异常的时候 try { ret = invocation.proceed(); } catch (Throwable throwable) { System.out.println("-----原始⽅法抛出异常 执⾏的额外功能 ----"); throwable.printStackTrace(); } //MethodInterceptor影响原始⽅法的返回值 原始⽅法的返回值,直接作为invoke⽅法的返回值返回,MethodInterceptor不会影响原始⽅法的返回值 MethodInterceptor影响原始⽅法的返回值 Invoke⽅法的返回值,不要直接返回原始⽅法的运⾏结果即可 @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("------log-----"); Object ret =invocation.proceed(); return false; }
2.切入点详解
切⼊点决定额外功能加⼊位置(⽅法)
<aop:pointcut id="pc" expression="execution(* *(..))"/>
exection(* *(..)) ---> 匹配了所有⽅法
a b c
1. execution() 切⼊点函数
2. * *(..) 切⼊点表达式
2.1 切⼊点表达式
-
⽅法切⼊点表达式
* *(..) --> 所有⽅法 * ---> 修饰符 返回值 * ---> ⽅法名 ()---> 参数表 ..---> 对于参数没有要求 (参数有没有,参数有⼏个都⾏,参数是什么类型的都⾏)-
定义login⽅法作为切⼊点
* login(..) # # 定义register register作为切⼊点 * register(..) -
定义login⽅法且login⽅法有两个字符串类型的参数 作为切⼊点
* login(String,String) #注意:⾮java.lang java.lang包中的类型,必须要写全限定名 * register(com.baizhiedu.proxy.User) # ..可以和具体的参数类型连⽤ * login(String,..) -->login(String),login(String,String),login(String,com.baizhiedu.proxy.User) -
精准⽅法切⼊点限定
修饰符 返回值 包.类.⽅ 法(参数) com.baizhiedu.proxy.UserServi ceImpl.login(..) com.baizhiedu.proxy.UserServi ceImpl.login(String,String)
-
-
类切⼊点
指定特定类作为切⼊点(额外功能加⼊的位置),⾃然这个类中的所有⽅法,都会加上对应的额外功能
- 语法1
## 类中的所有⽅法加⼊了额外功能
com.baizhiedu.proxy.UserServiceImpl.*(..)
-
语法2
## 忽略包 1. 类只存在⼀级包 com.UserServiceImpl * *.UserServiceImpl.*(..) 2. 类存在多级包 com.baizhiedu.proxy.UserServiceImpl * *..UserServiceImpl.*(..)
-
包切⼊点表达式 实战
指定包作为额外功能加⼊的位置,⾃然包中的所有类及其⽅法都会加⼊额外的功能-
语法1
## 切⼊点包中的所有类,必须在proxy proxy中,不能在proxy proxy包的⼦包中 * com.baizhiedu.proxy.*.*(..) -
语法2
## 切⼊点当前包及其⼦包都⽣效 * com.baizhiedu.proxy..*.*(..)
-
2.2 切⼊点函数
切⼊点函数:⽤于执⾏切⼊点表达式
-
execution
最为重要的切⼊点函数,功能最全。 执⾏ ⽅法切⼊点表达式 类切⼊点表达式 包切⼊点表达式 弊端:execution执⾏切⼊点表达式 , 书写麻烦 execution(* com.baizhiedu.proxy..*.*(..)) 注意:其他的切⼊点函数 简化是 execution书写复杂度,功能上完全⼀致 -
args
作⽤:主要⽤于函数(⽅法) 参数的匹配 切⼊点:⽅法参数必须得是2个字符串类型的参数 execution(* *(String,String)) args(String,String) -
within
作⽤:主要⽤于进⾏类、包切⼊点表达式的匹配 切⼊点:UserServiceImpl这个类 execution(* *..UserServiceImpl.*(..)) within(*..UserServiceImpl) execution(* com.baizhiedu.proxy..*.*(..)) within(com.baizhiedu.proxy..* ) -
@annotation
作⽤:为具有特殊注解的⽅法加⼊额外功能 <aop:pointcut id="" expression="@annotation(com.baizhiedu.Log)"/> -
切⼊点函数的逻辑运算
指的是 整合多个切⼊点函数⼀起配合⼯作,进⽽完成更为复杂的需求-
and与操作
案例:login 同时 参数 2个字符串 1. execution(* login(String,String)) 2. execution(* login(..)) and args(String,String) 注意:与操作不同⽤于同种类型的切⼊点函数 案例:register⽅法 和 login⽅法作为切⼊点 execution(* login(..)) or execution(* register(..)) -
or或操作
案例:register⽅法 和 login⽅法作为切⼊点 execution(* login(..)) or execution(* register(..))
-
第四章:AOP编程
1. AOP概念
AOP (Aspect Oriented Programing)
⾯向切⾯编程 = Spring动态代理开发
以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
## 切⾯ = 切⼊点 + 额外功能
OOP (Object Oritened Programing) ⾯向对象编程 Java
以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建
POP (Producer Oriented Programing) ⾯向过程(⽅法、函数)编程 C
以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建
## AOP的概念:
本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充
2. AOP编程的开发步骤
1. 原始对象
2. 额外功能 (MethodInterceptor)
3. 切⼊点
4. 组装切⾯ (额外功能+切⼊点)
3. 切⾯的名词解释
## 切⾯ = 切⼊点 + 额外功能
⼏何学
⾯ = 点 + 相同的性质

第五章:AOP的底层实现原理
1. 核⼼问题
1. AOP如何创建动态代理类(动态字节码技术)
2. Spring⼯⼚如何加⼯创建代理对象
通过原始对象的id值,获得的是代理对象
2. 动态代理类的创建
2.1 JDK的动态代理
- Proxy.newProxyInstance⽅法参数详解

-
编码
public class TestProxy02 { public static void main(String[] args){ PersonService personService = new PersonService(); //使用JDK的动态代理 //获取类加载器 ClassLoader classLoader = PersonService.class.getClassLoader(); //获取原始类实现的接口 Class<?>[] interfaces = personService.getClass().getInterfaces(); //获取invocationHandler的实现 InvocationHandler invocationHandler = (proxy, method, args1) -> { System.out.println("-----log-----"); Object ret = method.invoke(personService, args1); System.out.println("-----a-----"); return ret; }; PersonDao personServiceProxy = (PersonDao) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); personServiceProxy.register(); } }
2.2 CGlib的动态代理
CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,
这样既可以保证2者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)

-
CGlib编码
PersonService personService = new PersonService(); // 使用CGlib的动态代理 Enhancer enhancer = new Enhancer(); enhancer.setClassLoader(TestProxy02.class.getClassLoader()); enhancer.setSuperclass(personService.getClass()); enhancer.setCallback( (MethodInterceptor) (o, method, objects, methodProxy) -> { System.out.println("--------log--------"); Object ret = method.invoke(personService, objects); System.out.println("-------b------------"); return ret; }); PersonService personServiceProxy = (PersonService) enhancer.create(); personServiceProxy.register(); -
总结
1. JDK动态代理 Proxy.newProxyInstance() 通过接⼝创建代理的实现类 2. Cglib动态代理 Enhancer 通过继承⽗类创建的代理类
3. Spring⼯⼚如何加⼯原始对象
- 思路分析

-
编码
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object ret = method.invoke(bean, args); System.out.println("------log-----"); return ret; } }; return Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invocationHandler); } }<bean id="personServiceProxy" class="com.muss.service.PersonService"/> <bean id="myBeanPostProcessor" class="com.muss.factory.MyBeanPostProcessor"/>
第六章:基于注解的AOP编程
1. 基于注解的AOP编程的开发步骤
-
原始对象
-
额外功能
-
切入点
-
组装切面
// 通过切⾯类 定义了 额外功能 @Around 定义了 切⼊点 @Around("execution(*login(..))") @Aspect 切⾯类 public class MyAspect { @Pointcut("execution(* register(..))") public void myPointcut() {} @Around("myPointcut()") public Object around(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); System.out.println("111"); return ret; } }<bean id="arround" class="com.baizhiedu.aspect.MyAspect"/> <!--告知Spring基于注解进⾏AOP编程--> <aop:aspectj-autoproxy />
2. 细节
-
切⼊点复⽤
切⼊点复⽤:在切⾯类中定义⼀个函数上⾯@Pointcut注解 通过这种⽅式,定义切⼊点表达式,后续更加有利于切⼊点复⽤。 @Pointcut("execution(* register(..))") public void myPointcut() {} @Around("myPointcut()") # 在切面类中编写一个返回类型为void,方法体为空,参数为空的方法上添加注解@Pointcut # 之后就可以在@Around注解中添加有@Pointcut的方法的方法名 -
动态代理的创建⽅式
AOP底层实现 2种代理创建⽅式 1. JDK 通过实现接⼝ 做新的实现类⽅式 创建代理对象 2. Cglib通过继承⽗类 做新的⼦类 创建代理对象 # 默认情况 AOP编程 底层应⽤JDK动态 代理创建⽅式 如果切换Cglib 1. 基于注解AOP开发 <aop:aspectjautoproxy proxy-target-class="true" /> 2. 传统的AOP开发 <aop:config proxytarget-class="true"></aop>
第七章:AOP开发中的⼀个坑
坑:在同⼀个业务类中,进⾏业务⽅法间的相互调⽤,只有最外层的⽅法,才是加⼊了额外功能(内部的⽅法,通过普通的⽅式调⽤,都调⽤的是原始⽅法)。
如果想让内层的⽅法也调⽤代理对象的⽅法,就要AppicationContextAware获得⼯⼚,进⽽获得代理对象
public class UserServiceImpl implements UserService,ApplicationContextAware {
private ApplicationContextctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throwsBeansException {
this.ctx =applicationContext;
}
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务运算 + DAO ");
UserService userService = (UserService)ctx.getBean("userService");
userService.login("suns","123456");
}
第八章:AOP阶段知识总结

Spring学习总结 — 持久层整合
第一章:持久层整合
1. Spring框架为什么要与持久层技术进行整合
1. JavaEE开发需要持久层进行对数据库的访问操作
2. JDBC Hibernate MyBatis进行持久层开发过程中存在大量的代码冗余
3. Spring基于模板设计模式对于上述的持久层技术进行了封装
2. Spring可以于那些持久层框架进行整合
1. JDBC
|- JDBCTemplate
2. Hibernate(JPA)
|- HibernateTemplate
3. MyBatis
|- SqlSessionFactoryBean MapperScannerConfigure
第二章:Spring与MyBatis整合
1. MyBatis开发步骤回顾
1. 实体
2. 实体别名
3. 表
4. 创建DAO接口
5. 实现Mapper文件
6. 注册Mapper文件
7. MyBatisAPI调用
2. MyBatis在开发过程中存在的问题
1. 配置繁琐
2. 代码冗余
3. Spring与MyBatis整合思路分析

4. Spring和MyBatis整合的开发步骤
-
配置文件(applicaitonContext.xml)中
配置dataSource的bean 配置SqlSessionFactoryBean的bean 配置MapperSacnnerConfigure的bean -
编码
1. 实体
2. 表
3. 创建DAO接口
4. 实现Mapper文件
5.Spring和MyBatis整合编码
-
添加依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> -
配置文件
<!-- 数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///test"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- 创建SqlSessionFactoryBean --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.muss.bean"/> <property name="mapperLocations"> <list> <value>classpath:*Mapper.xml</value> </list> </property> </bean> <!-- 创建MapperScannerConfigure --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <property name="basePackage" value="com.muss.dao"/> </bean> -
编码
// 实体 public class Person { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "Person{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } } //dao文件 public interface PersonDao { public void save(Person person); } //mapper文件 <mapper namespace="com.muss.dao.PersonDao"> <insert id="save" parameterType="Person"> insert into person (username,password) values (#{username},#{password}); </insert> </mapper>
6.Spring与MyBatis整合细节
-
问题:Spring和MyBatis整合之后,为什么DAO不提交事务,但是数据库能够插入数据呢
Connection --> tx # 本质上控制连接对象(Connection)--> 连接池(DataSource) 1. MyBatis提供的数据库连接池 --> 创建Connection Connection.setAutoCommit(false) 手工控制事务,需要在操作完成后手动提交 2. Druid(C3P0)作为连接池 --> 创建Connection Connection.setAutoCommit(true) true默认值,保持自动控制事务,自动提交 # 注意:未来实战中,使用Spring通过控制事务解决多条sql一起成功一起失败的问题
第三章:Spring的事务处理
什么是事务:保证业务操作完整性的一种数据库机制
- 事务的四个特点:A C I D
- A :原子性
- C : 一致性
- I :隔离性
- D :持久性
1. 如何控制事务
JDBC:
Connection.setAutoCommit(false)
Connection.commit();
Connection.rollback();
Mybatis:
Mybatis自动开启事务
sqlsession(Connection).commit()
sqlsession(Connection).rollback()
# 结论:控制事务的底层,都是通过Connection对象完成的
2. Spring控制事务的开发
Spring是通过AOP的方式进行事务开发
-
原始对象
public class UserServiceImpl(){ private UserDAO userDAO; set, get 1. 原始对象-->原始方法-->核心功能(业务处理+核心功能调用) 2. Dao作为Service的成员变量,通过依赖注入的方式进行赋值 } -
额外功能
1. MethodInterceptor public Object invoke(MethodInvocation invocation){ Object ret = null; try{ Connection.setAutoCommit(false); ret = invocation.proceed() Connection.commit(); }catch(Exception e){ Connection.rollback(); } return ret } 2. @Aspect @Around # spring封装了 DateSourceTransactionManager,进行事务管理但是我们需要为其注入连接(注入连接池DateSource) -
切入点
@Transaction 事务的额外功能加给哪些业务方法 1. 类上:类中所有的方法都会加入事务 2. 方法上:这个方法会加入事务 -
组装切面
切入点 额外功能 <tx:annotation-driven transction-manger="dataSourceTransactionManger"/> <bean id="userService" class="com.gewei.service.UserServiceImpl"> <property name="dao" ref="userDao"/> </bean> <bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dateSource"/> </bean> <tx:annotation-driven transaction-manager="dateSourceTransactionManger"/> -
细节
<tx:annotation-driven transaction-manager="dateSourceTransactionManger" proxy-target-class="true"/> 进行动态代理底层实现的切换 默认 false JDK true CGlib
第四章:Spring事务属性
事务属性:描述事务特征的一系列值
# 隔离属性
# 传播属性
# 只读属性
# 超时属性
# 异常属性
-
如何添加事务属性
@Transactional(isolation = ,propagation = , readOnly = ,timeout = , rollbackFor = ,noRollbackFor = ) -
隔离属性:他表示了事务解决并发问题的特征
-
什么是并发
多个事务,或者用户,在同一时间访问操作了统一数据(同一时间:并不是说必须分毫不差,可能差0.00几秒) -
并发会产生哪些问题
- 脏读
- 不可重复读
- 幻影读
-
并发问题如何解决
通过隔离属性解决,隔离属性中设置不同的值,解决并发 处理过程中的问题
1. 隔离属性
脏读
一个事务,读取了另一个事务没有提交的数据,会产生数据不一致的问题
解决办法:读已提交 @Transactional(isolation = Isolation.READ_COMMITTED)
不可重复读
一个事务,多次读取相同的数据,但是读取结果不一致,会在本事务中产生数据不一致的问题
注意:1 不是脏读 2 在一个事务中
解决方法:@Transactional(isolation = Isolation.REPEATABLE_READ)
本质:一把行锁
幻影读
一个事务中,多次对整表进行查询统计,但是结果不一样,会在 本事务中产生数据不一致的问题。
解决方法:@Transactional(isolation = Isolation.SERIALIZABLE)
本质:表锁
总结
并发安全:SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
运行效率:READ_COMMITTE>REPEATABLE_READ>SERIALIZABLE
数据库对隔离属性的支持
| 隔离属性的值 | Oracle | Mysql |
|---|---|---|
| isolation = Isolation.READ_COMMITTED | 可以 | 可以 |
| Isolation.REPEATABLE_READ | 不可以 | 可以 |
| Isolation.SERIALIZABLE | 可以 | 可以 |
Oracle不支持REPEATABLE_READ值,如何解决不可重复读采用多版本的的方式,解决不可重复读的问题
默认隔离属性
-
ISOLATION_DEAFULT :会调用不同数据库设置的隔离属性
-
Mysql:不可重复读 REPEATABLE_READ
- 可以通过sql语句查询: SELECT @@tx_isolation
-
Oracle:读已提交 READ_COMMITTED
-
隔离属性在实战中的建议:直接使用默认就好,在未来的实战中,并发访问的几率很低如果真的遇到并发问题,推荐使用乐观锁。
- Hibernate(JPA) Version
- Mybatis:通过拦截器自定义开发
2. 传播属性(propagation)
- 传播属性:描述了事务解决嵌套问题的特征
- 什么是事务的嵌套:它指的是一个大的事务中,包含若干个小的事务
- 问题:大事务中融入了很多小的事务,他们彼此影响,最终会导致外部大的事务,丧失了事务的原子性
| 传播属性的值 | 外部不存在事务 | 外部存在事务 | 值 | 备注 |
|---|---|---|---|---|
| REQUIRED | 开启新的事务 | 融入外部事务 | Propagation.REQUIRED | 增删改操作 |
| SUPPORTS | 不开启事务 | 融入外部事务 | Propagation.SUPPORTS | 查询方法 |
Required是查询属性的默认值
查询:显示的指定传播属性为Supports
3. 只读属性
针对于进行查询操作的方法,可以加入只读属性,提高运行效率
默认值为false
4.超时属性
当前事务访问数据时,有可能访问的数据被其他数据获取锁后加锁了,当前事务就必须要等待释放锁。
超时属性默认值为-1,最终由数据库指定(在开发中很少使用)
5. 异常属性
Spring事务处理过程中
默认 对RuntimeException及其子类,采用的是回滚的机制
默认 对Exception及其子类,采用的是提交事务的机制
rollbackFor = java.lang.Exception Exception开启回滚
noRollbackFor = java.lang.RuntimeEexception 关闭回滚
建议:实战中使用默认就可以了
6. 基于标签的事务配置方式(事务开发的第二种形式)
基于标签的事务配置
1. 原始类
...
2. 额外功能与事务属性
...
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="register"/>
<tx:method name="login" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 等效于
@Transactional
public void register(User user) {...}
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public void login(String username, String password) {...}
-->
<aop:config>
3. 切入点
<aop:pointcut id="pc" expression="execution(* com.angenin1.service.UserServiceImpl.*(..))"/>
4. 组装切面
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>
-
基于标签的事务配置在实战中的引用方式
事务属性配置 <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager"> <tx:attributes> <!--<tx:method name="register"/>--> <!--<tx:method name="login" propagation="SUPPORTS" read-only="true"/>--> <!--使用通配符解决tx:method标签过多的问题--> <!--编程时,service中负责增删改操作的方法都以modify开头即可--> <tx:method name="modify*"/> <!-- *:指除上面的其他方法(查询),需要把范围小的放前面,范围大的放后面 --> <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> 切入点 <aop:config> <!--<aop:pointcut id="pc" expression="execution(* com.angenin1.service.UserServiceImpl.*(..))"/>--> <!--使用包切入点--> <aop:pointcut id="pc" expression="execution(* com.angenin1.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/> </aop:config>
Spring学习总结 — 注解编程
Spring开发过程中多配置文件的处理
Spring根据需要,将配置文件分门别类放在多个配置文件中,便于后期管理和维护
DAO --> applicaiton-dao.xml
Service --> application-service.xml
Controller --> application-controller.xml
-
通配符方式
1. 非web环境下 ApplicaitonCOntext ac = new ClassPathXmlApplicationContext("/application-*.xml") 2. web环境下 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application-*.xml</param-value> </context-param> -
import标签
application.xml <import resource="application-dao.xml"/> <import resource="application-service.xml"/> <import resource="application-controller.xml"/> 1. 非web环境下 ApplicaitonCOntext ac = new ClassPathXmlApplicationContext("/application.xml") 2. web环境下 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:application.xml</param-value> </context-param>第一章:注解基础概念
1. 什么是注解编程
指的是在类或方法上加入特定的注解(@XXX),完成特定功能的开发
@Component
public class XX(){}
2. 为什么要使用注解
1. 注解开发方便
代码简洁 开发速度大大提高
2. Spring开发潮流
3. 注解的作用
- 替换XML这种配置形式,简化配置

-
替换接口,实现调用双方的契约性
通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更加灵活

4. Spring注解开发的一个问题
Spring基于注解进行配置后,还能解耦合吗?
在Spring框架应用注解时,如果对注解配置的内容觉得不满意,可以使用Spring配置文件的方式进行覆盖
第二章:Spring的基本注解
1. 对象创建相关注解
-
搭建开发环境
<context:component-scan base-package="包名"/> 作用:让Spring框架在设置包及其子包中扫描对应的注解,使其生效
@Component
作用:替换Spring配置文件中的bean标签
注意:
id属性:component注解 提供了默认的设置方式 首单词字母小写
class属性:通过反射获取class内容

-
@Component细节
-
如何显示指定工厂创建对象的id值
@Component("想要输入的id值") -
Spring配置文件覆盖注解内容
applicationx.xml <bean id="XXX" class="xxx"/> 注意:id值 class值 要和 注解中的设置保持一致 -
@Component的衍生注解
@Repository @Serrvice @Controller 注意:本质上衍生注解就是 @Component 目的:更加准确的表达一个类型的作用 注意:Spring与MyBatis整合过程中,@Repository @Component不会使用
-
@Scope
作用:控制简单对象创建的次数
@Scope
public class XX(){}
@Lazy
作用:延迟创建单实例对象
注意:一旦使用这个注解后,Spring会在使用这个对象时才创建
@Lazy
public class XX(){}
2. 生命周期方法相关注解
1. 初始化方法 @PostConstruct
InitializingBean
<bean init-method=""/>
2. 销毁方法 @PreDestory
DisposableBean
<bean destory-method=""/>
注意:
1 上述的2个注解是由JSR(JavaEE规范)520 提供的
2 再一次验证,通过注解实现接口的契约性
3. 注入相关注解
- 用户自定义类型 @Autowired

@Autowired细节
1. Autowired注解基于类型注入【推荐】
基于类型注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(接口实现)
2. Autowired Qualifier 基于名字进行注入【了解】
基于名字注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
3. Autowired注解的放置位置
a) 放置在对应的成员变量set方法上
b) 直接放置在成员变量上,Spring通过反射直接对成员变量进行注入【推荐】
4. JavaEE规范类似的注解
JSR250 中基于名字注入
@Resource(name="xxx") 等同于 @Autowired + @Qualifier(name="xxx")
注意:如果在应用Resource注解时,名字没有配对成功,那么会按照类型进行注入
JSR330 中@Inject 作用与 @Autowired 完全一致 一般在EJB中使用
想使用的话需要引入相关依赖
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
-
JDK类型
-
@Value
@Value注解完成步骤 1. 设置xxx.properties key = value id = 1 2. Spring的工厂读取这个配置文件 <context:property-placeholder location=""/> 3. 代码 属性 @Value()-
@Value细节
-
@Value注解不能应用在静态成员变量
如果应用,便会注入(赋值)失败 -
@Value注解+Properties这种方式,不可以注入集合类型
-
-
-
@PropertySource
作用:用于代替Spring配置文件中<context:property-placeholder location=""/>的 1. 设置xxx.properties key = value id = 1 2. 应用@PropertySource @PropertySource("classpath:/xx.properties") public class xx(){} 3. 代码 属性 @Value()
-
4. 注解扫描详解
<context:component-scan base-package="xxx"/>
-
排除方式
<context:component-scan base-package="com.muss"> <context:exclude-filter type="" expression=""/> type: assignable: 排除特定的类型 不进行扫描 anntation: 排除特定的注解 不进行扫描 aspectj: 切入点表达式排除 regex: 正则表达式排除 custom: 自定义排除册罗 一般在框架底层开发 </context:component-scan> -
包含方式
<context:component-scan base-package="com.muss" use-default-filters="false"> <context:include-filter type="" expression=""/> </context:component-scan> 1. use-default-filters 作用:让Spring的默认注解扫描方式失效 2. <context:include-filter type="" expression=""/> 作用:指定扫描那些注解 type: assignable: 指定特定的类型 进行扫描 anntation: 指定特定的注解 进行扫描 aspectj: 切入点表达式扫描 regex: 正则表达式扫描 custom: 自定义扫描策略 一般在框架底层开发 包含的方式支持叠加
5. 对于注解的思考
-
配置互通
注解配置 Spring配置文件 互通 -
什么情况使用注解 什么情况下使用配置文件
基础注解 程序员开发类型的配置 1. 在程序员开发的类型上 可以加入对应注解 2. 应用非程序员开发的类型是,还是需要使用<bean>进行配置
第三章:Spring的高级注解
1. 配置Bean
Spring3.X提供的新注解,用于代替XML配置文件
-
@Configuration注解的本质
本质:也是@Component注解的衍生注解
2. @Bean
1. @Bean注解的基本使用
- 对象的创建

1. 简单对象的创建
直接能够通过new方式创建的对象
2. 复杂对象的创建
不能通过new方式创建的对象
-
@Bean注解创建复杂对象的注意事项
@Bean public Connection conn(){ ConnectionFactoryBean cfb = new ConnectionFactoryBean(); Connection conn = cfb.getObject(); return conn; } -
自定义id值
@Bean("id") -
控制对象的创建次数
@Bean @Scope("singleton|prototype") 默认值 singleton
2. @Bean注解的注入
-
用户自定义类型的注入
@Bean public UserService userService(){ UserServiceImpl usi = new UserServiceImpl(); usi.setUserDao(userDao());//userDao()是另一个有@Bean的方法 return usi; } -
JDK的注入
@Bean public Customer customer(){ Customer customer = new Customer(); customer.setId(1); customer.setName("qiqi"); return customer; }-
JDK类型注入细节分析
//如果直接在代码中使用set方法调用会产生耦合 @Configuration @PorpertySource("classpath:/init.properties") public class MyConifg{ @Value("${id}") private int id; @Bean public Customer customer(){ Customer customer = new Customer(); customer.setId(id); customer.setName("qiqi"); return customer; } }
-
3. @ComponentScan
@ComponentScan在注解配置中等同与 <context:component-sacn>标签
目的:进行相关注解的扫描
-
基本使用
@Configuration @ComponentScan(basePackages="xxx") public class MyConifg{} -
排除,包含的使用
- 排除

- 包含

4. Spring工厂创建对象的多种配置方式
1. 多种配置方式的应用场景

2. 配置的优先级
@Component及其衍生注解 < @Bean < bean标签
# 优先级高的覆盖优先级低的 但id值需要保持一致
- 解决注解配置产生耦合的问题

5. 整合多个配置信息
-
为什么会有多个配置信息
拆分多个配置bean的开发,是一种模块化的开发,也体现了面对对象各司其职的设计思想 -
多个配置信息整合的方式
- 多个配置bean的整合
- 配置bean与@Component相关注解的整合
- 配置bean与SpringXML配置文件的整合
-
整合多个配置需要关注的那些要点
- 如何使用多配置信息汇总成一个整体
- 如何实现跨配置的注入
1. 多个配置Bean的整合
-
多配置的信息汇总
- base-package进行多个配置Bean的整合

-
@Import
1. 可以创建对象 2. 多配置bean的整合

-
在工厂创建时,指定多个配置bean的Class对象【了解】
ApplicaitonContext ac = new AnnotationConfigApplicaitonContext(MyConfig.class,MyConfig2.class); -
跨配置的注入

2. 配置Bean和@Component及其衍生注解整合

3. 配置Bean与配置文件整合

6. 配置Bean底层实现原理
Spring在配置Bean中加入@Configuration注解后,底层就会通过CGlib的代理方式,来进行对象相关的配置.处理

7. 四维一体的开发思想
1. 什么是四维一体
Spring开发一个功能的四种方式,虽然开发方式不同,但是结果是一样的
1. 基于schema
2. 基于特定功能注解
3. 基于原始bean标签
4. 基于@Bean注解
8. 纯注解AOP编程
- 开发步骤

- 注解AOP细节分析

9. 纯注解事务编程

本文详细介绍了Spring框架的学习,从引言开始,讲解了Spring的核心API、工厂设计模式,以及Spring与其他日志框架的整合。深入探讨了注入、Set注入、构造注入等,并分析了Spring的事务处理和持久层整合,特别是Spring与MyBatis的整合。此外,还涵盖了Spring的注解编程,包括基本注解和高级注解的应用,以及事务和AOP编程。通过对Spring各个重要特性的全面解析,帮助读者掌握Spring框架的精髓。
3067

被折叠的 条评论
为什么被折叠?



