Spring框架笔记
Spring框架笔记
IOC容器(控制反转)
什么是 IOC
控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。
使用IOC目的:
降低耦合度
通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象引用传递给他。也可以说依赖被注入到对象中。
IOC 底层原理
工厂模式(解耦操作),反射(得到.class文件),xml解析
工厂解耦过程:
IOC 解耦过程
-
xml配置文件,配置创建的对象
<bean id="dao" class="com.company.UserDao"></bean>
-
有service和dao类,创建工厂类
//创建工厂类 class UserFactory{ public static UserDao getDao(){ //xml解析 String classValue = class属性值; //通过反射创建对象 Class class = Class.forName(classValue); return (UserDao)class.newInstance; } }
IOC (接口)
-
IOC思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
-
Spring提供了IOC容器实现两种方式:(两个接口)
(1)BeanFactory:IOC容器里面最基本的实现方式,是Spring内部的接口,不提供开发人员使用
特点:加载配置文件时,不会创建对象,在获取或者使用的时候才会创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
特点:加载配置文件的时候就会把配置文件对象进行创建
-
ApplicationContext接口有实现类(ctrl + h查看接口)
IOC 操作 Bean 管理
-
什么是 Bean 管理
(1)Spring 创建对象
(2)Spring 注入属性
-
Bean 管理操作有两种方式
(1)xml配置文件实现
(2)基于注解方式实现
基于xml方式
-
基于xml创建对象
<!--配置User对象创建--> <bean id="user" class="com.company.User"></bean>
(1)在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
(2)在bean标签有很多属性,介绍常用的属性
- id 属性:唯一标识
- class 属性:创建对象所在类的全路径(包类路径)
(3)创建对象时, 默认也是执行无参构造方法
-
基于xml方式注入属性
(1)DI:依赖注入,就是注入属性。是IOC的一种具体实现方式。
-
第一种注入方式:
使用set方法注入
public void setUserName(String name){ this.name = name; } //main方法中调用创建UserName并调用set方法 UserName username = new UserName(); username.setUserName("jack");
在spring配置文件配置对象创建,配置注入属性(xml中的配置)
<bean id="UserName" class="com.company.UserName"> <!--使用property完成属性注入 name:类里面属性名称 value:想属性里面注入的值 --> <property name="UserName" value="jack"></property> </bean>
-
第二种注入方式:
有参构造注入
UserName userName = new UserName("jack");
具体步骤:
(1)创建类,定义属性,创建属性对应有参构造方法。
// 1.加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml") // 2.获取配置创建的对象 Orders orders = context.getBean("odders",Orders.class)
(2)在spring配置文件(xml中的配置)配置对象创建,配置注入属性
<!--有参构造注入--> <bean id="UserName" class="com.company.UserName"> <constructor-arg name="UserName" value="jack"></constructor-arg> </bean>
(2)注入空值特殊符号
// 注入空值 <null/> //特殊符号 1.把<>进行转义 <> 2.把带特殊符号的内容写到CDATA中 <property name="address"><value><![CDATA[<<南京>>]]></value></property>
(3)注入属性-外部bean
-
创建两个类service类和dao类
//在xml中service和dao对象创建 <bean id="userService" class="com.sompany.service.UserService"> //注入userDao对象 name属性:类里面属性名称 ref属性:创建userDao对象bean标签id值 // ref里面的名称要和id 的userDao一样 <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.company.dao.UserDaoImpl"></bean>
-
在service调用dao里面的方法
-
在spring配置文件中进行配置
(4)注入属性-外部bean和级联赋值
-
xml注入集合属性
-
注入数组类型属性
<property name="course"> <array> <value>java</value> <value>mysql</value> </array> </property>
-
注入List集合类型属性
<property name="list"> <list> <value>lgm</value> <value>lgm</value> </list> </property>
-
注入Map集合类型属性
<property name="maps"> <map> <entry key="JAVA" value="java"></entry> <entry key="PHP" valie="php"></entry> </map> </property>
-
注入set集合类型属性
<property name="set"> <set> <value>....</value> <value>....</value> </set> </property>
-
在集合里面设置对象类型值
-
建立类
public class Course{ ....... }
-
配置文件注入
<property name="courseList"> <list> <ref bean=""></ref> </list> </property> <bean id="course1" class="com.company.lgm.course"> <proprety name="cname" value="Mybatis框架"></proprety> </bean>
-
-
把集合注入部分提取出来
- 在spring配置文件中引入名称空间 util
FactoryBean
- Spring有两种类型 bean ,一种普通 bean ,另外一种是工厂 bean (FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂 bean ,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现方法中定义返回的 bean 类型
bean 的作用域
-
在 Spring 里面,可以设置创建的 bean 实例是单实例还是多实例
-
在 Spring 里面,默认情况下,创建的 bean 是单实例对象(可以通过设置成多实例)
-
如何设置单实例还是多实例
(1)在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
(2)scope属性值:
第一个值,singleton,表示是单实例对象
第二个值,prototype,表示是多实例对象
<bean id="" class="" scope="singleton" 或者 scope="prototype"></bean>
bean 的生命周期
-
从对象创建到对象销毁的过程
-
bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 把 bean 实例传递 bean 后置处理器的方法
- 调用bean的初始化的方法(需要进行配置初始化方法)
- 把 bean 实例传递 bean 后置处理器的方法
- bean 可以使用了(对象获取到了)
- 当容器关闭的时候,调用bean的销毁方法(想要进行配置销毁的方法)
-
演示bean生命周期
//创建类 java文件 public class Orders{ //无参构造 public Orders(){ System.out.println("第一步 执行无参数构造创建bean实例"); } private String oname; public void setOname(String oname){ this.oname = oname; System.out.println("第二步 调用set方法设置属性值"); } //创建初始化的方法 public void initMethod(){ System.out.println("第三部 执行初始化的方法"); //在bean中加入init-methid标签写入initMethod方法 } //创建销毁的方法 public void destroyMethod(){ System.out.println("第五部 执行销毁的方法"); //在bean中加入destroy-methid标签写入destroyMethod方法 } } //写方法调用 public void testBean(){ ApplicationContext context = new ClassPathXmlApplicationContext(bean.xml); Orders oeders = context.getBean("oeders",Orders.class); System.out.println("第四部 获取到bean的"); System.out.println(oeders); //手动让bean实例销毁 context.close(); }
//配置bean.xml文件 <bean id="orders" class="..." init-methid="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean>
-
演示添加后置处理器
-
创建类,实现接口 BeanPostProcessor,创建后置处理器 17集 21:00
public class MyBeanPost implements BeanPostProcessor { }
-
Xml的自动装配
-
什么是自动装配
- 根据指定装配规则(属性名称或者属性类型),spring自动将匹配的属性值进行注入
-
演示自动装配过程
外部属性文件
-
直接配置数据库的信息
-
配置德鲁伊连接池
-
引入德鲁伊连接池依赖jar包
<bean id="dataSource" class="...路径..."> <property name="driverClassName" value=""></property>//数据库驱动 <property name="url" value="jdbc:mysql://locahost:3306/userDb"></property>//数据库地址 <property name="username" value="root"></property>//数据库用户名 <property name="password" value="root"></property>//数据库密码 </bean>
-
-
引入外部属性文件配置数据库连接池
-
创建外部属性文件,properties 格式文件,写入数据库信息
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://locahost:3306/userDb prop.password=root prop.passName=root
-
把外部properties属性文件引入到spring配置文件中
1.引入context名称空间
<beans xmlns:context="http://www.springframework.org/schema/context"> </beans>
2.引入外部属性文件
<context:property-placeholder location="classpath:jdbc.properties"/> //配置连接池 value值写表达式,不写定值 <bean id="dataSource" class="...路径..."> <property name="driverClassName" value="${prop.driverClass}"></property>//数据库驱动 <property name="url" value="${prop.url}"></property>//数据库地址 <property name="username" value="${prop.password}"></property>//数据库用户名 <property name="password" value="${prop.passName}"></property>//数据库密码 </bean>
-
基于注解方式
什么是注解
注解是代码里面的特殊标记。格式:@注解名称(属性名称=属性值,属性名称=属性值)
-
使用注解,注解在什么地方用。
类上面,方法上面,属性上面
-
使用注解目的:简化xml配置
Spring针对bean管理中创建对象提供注解
- @component
- @Service
- @Controller
- @Repository
四个注解功能是一样的,都可以用来创建bean实例
-
基于注解方式实现对象创建
第一步 引入依赖 jar包
第二步 开启组件扫描
//如果扫描多个包,中间可以用逗号隔开 //扫描包上层目录(所以文件全部扫描) <context:component-scan base-package="com.atguigu.spring5.dao,com.atguigu.spring5.service"></context:component-scan>
第三步 创建类,在类上面添加创建对象注解
@component(value = "userService") public class UserService{ public void add(){ System.out.println("service add ..."); } }
第四部 组件设置扫描那些文件 context.include-filter
//部分扫描 include-filter <context:component-scan base-package="com.atguigu" use-default-filters="false"> <context.include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> //部分不扫描 exclude-filter <context:component-scan base-package="com.atguigu"> <context.exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
基于注解方式实现属性注入
-
@AutoWired :根据属性类型进行自动装配
第一步 把 service 和 dao 对象创建,在service 和 dao 类添加创建对象的注解
第二步 在 service注入 dao 对象,在service 类添加 dao 类型属性,在属性上面使用注解
-
@Qualifier :根据属性名称进行注入
@Qualifier注解的使用和@AutoWired一起使用
-
@Resource :根据类型注入,可以根据名称注入
import javax.annotation.Resource; @Resource (name = "userDaoImpl") //根据名称进行注入 private UserDao userDao;
-
@Value :注入普通类型属性
@value(value = "abc") private String name;
-
完全注解开发
import org.apringframework.context.annotation.Configuration;
//创建配置类,替代xml配置文件
@Configuration
@componentScan(basePackages = {"com.atguigu"})
public void Configuration{
}
//编写测试类
public void testService(){
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService",UserService.class);
userService.add();
}
Aop
什么是Aop
面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离。从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
AOP底层原理
-
AOP底层使用动态代理
1.有两种情况的动态代理
-
有接口情况,使用JDK动态代理
-
没有接口情况,使用CGLIB动态代理
创建子类的代理对象,增强类的方法
-
JDK动态代理
使用JDK动态代理,使用 Proxy 类里面的方法创建代理对象,调用 newProxyInstance 方法
方法里面有三个参数,
第一个参数:ClassLoader 类加载器
第二个参数:类<?>[ ] interfaces 增强方法所在的类,这个类实现的接口,支持多个接口
第三个参数:InvocationHandler 实现这个接口,创建代理对象,写增强的方法
newProxyInstance(ClassLoader loader , 类<?>[ ] interfaces ,InvocationHandler h)
创建接口,定义方法
package com.company.spring5;
//UserDao接口
public interface UserDao {
public int add(int a, int b);
public String upadte(String id);
}
创建接口实现类 UserDaoImpl,实现方法
package com.company.spring5;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String upadte(String id) {
return id;
}
}
使用 Proxy
package com.company.spring5;
import java.util.Arrays;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类的代理对象
Class[] interfaces = {UserDao.class};
// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return null;
// }
// });
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
int result = dao.add(1,2);
System.out.println("result:"+ result);
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler{
//1.把创建的是谁的代理对象,把谁传递过来
//有参数的构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj;
}
//增强逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行...."+ method.getName()+"传递的参数"+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj,args);
//方法之后
System.out.println("方法之后执行"+obj);
return res;
}
}
AOP术语
-
连接点
类里面那些方法可以被增强,这些方法称为连接点
-
切入点
实际被真正增强的方法,成为切入点
-
通知(增强)
(1)实际增强的逻辑部分成为通知(增强)
(2)通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知 finally
-
切面
是动作
- 把通知应用到切入点过程
AOP操作(准备)
-
Spring框架中一般基于AspectJ实现AOP操作
(1)什么是AspectJ
- AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和 Spring 框架一起使用,进行 AOP 操作
-
基于AspectJ 实现 AOP 操作
-
基于xml配置文件实现
-
基于注解方式实现(使用)
-
-
在项目工程里面引入 AOP 相关依赖 jar包
-
切入点表达式
-
切入点表达式作用:知道对哪个类型里面的哪个方法进行增强
-
语法结构:
execution([权限修饰符][返回类型][类全路径][方法名称][参数列表]) //举例1 对com.atguigu.dao.BookDao类里面的 add 进行增强 excution(*com.atguigu.dao.BookDao.add(..))// *表示任意修饰符 //举例2 对com.atguigu.dao.BookDao类里面的所有方法进行增强 excution(*com.atguigu.dao.BookDao.*(..)) //举例3 对com.atguigu.dao包里面所有类,类里面所有方法进行增强 excution(*com.atguigu.dao.*.*(..))
-
AspectJ注解
-
创建类,在类里面定义方法
//被增强类 public class User { public void add(){ System.out.println("add...."); } }
-
创建增强类(编写增强逻辑)
//增强类 public class UserProxy { //前置通知 public void before(){ System.out.println("before....."); } }
-
进行通知的配置
(1)在spring配置文件中,开启注解扫描
<!--开启注解扫描--> <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
(2)使用注解创建 User 和 UserProxy 对象
import org.springframework.stereotype.Component; //被增强类 @Component public class User{ public void add(){ System.out.println("add...."); } } //增强类 @Component @Aspect //第(3)步 public class UserProxy { //前置通知 //@Before注解表示作为前置通知 @Before(value="execution(*com.atguigu.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before....."); } }
(3)在增强类上面添加注解@Aspect
(4)在 spring 配置文件中开启生成代理对象
<!--开启 Aspect 生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型的注解,使用切入点表达式配置内容
AspectJ配置文件
- 创建两个类,增强类和被增强类,创建方法
- 在spring 配置文件中创建两个类对象
- 在 spring 配置文件中配置
JdbcTemplate
什么是JdbcTemplate
- Spring框架对JDBC进行封装,使用 JdbcTemplate 方便实现对数据库操作
- CSDN相关博客
准备工作
-
引入相关jar包
-
[在Spring配置文件配置数据库连接池]
<!--数据库连接池--> <bean id="dataSource" class="com.alibaba.druild.pool.DruidDataSource" destroy-method="close"> <property name="url" value="jdbc:mysql://user_db"/> <property name="uesrname" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> </bean>
-
配置 JdbcTemplate
<!--JdbcTemplate对象--> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入dataSource--> <property name="dataSource" ref="dataSource"></property> </bean>
-
创建 service 类,创建dao 类,在 dao 注入 jdbcTemplate 对象
//xml中配置 <context:component-scan base-package="com.atguigu"></context:component-scan> //Java文件中 @service public class BookService{ //注入dao @Autowired private BookDao bookDao; } // Dao @Repository public class BookDaoImpl implements BookDao{ //注入 JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; }
JdbcTemplate操作数据库
增加,删除
-
创建类和实体方法
package com.company.entity; public class User { private String userId; private String username; private String ustatus; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUstatus() { return ustatus; } public void setUstatus(String ustatus) { this.ustatus = ustatus; } }
-
编写 service 和 dao
(1)在 dao 进行数据库添加操作
(2)调用 JdbcTemplate 对象里面 update 方法实现添加操作
有两个参数
第一个参数: sql语句
第二个参数: 可变参数,设置 SQL 语句值
//修改 @Override public void updateBook(Book book){ String aql="update t_book set username=?,ustatus=? where user_id=?" Object[] args = {book.getUsername(),book.getUstatus(),book.getUserId()}; int update = JdbcTemplate.update(sql,args); System.out.println(update); }
查询返回某个值
-
查询表里面有多少条记录,返回是某个值
-
使用 JdbcTemplate 实现查询返回某个值代码
有两个参数
第一个参数:SQL语句
第二个参数:返回类型Class
查询返回对象
-
场景:查询图书详情
-
JdbcTemplate 实现查询返回对象
有三个参数
第一个参数:SQL语句
第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
第三个参数:SQL语句值
查询返回集合
- 场景:查询图书列表分页…
- 调用 JdbcTemplate 方法实现查询返回集合
批量添加操作
-
批量操作:操作表里面多条记录
-
JdbcTemplate 实现批量添加操作
batchUpdate(String sql,List<Object[]> batchArgs)
两个参数
第一个参数:SQL语句
第二个参数:List 集合,添加多条记录数据
批量修改删除操作
事务管理
什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果一个失败所有操作都失败
- 最典型场景:银行转账
- 事务四个特性(ACID)
- 原子性
- 一致性
- 隔离性
- 持久性
搭建事务操作环境
-
创建数据库表,添加记录
-
创建 service ,搭建 dao ,完成对象创建和注入关系
(1)service 注入 dao ,在 dao 注入JdbcTemplate ,在 JdbcTemplate注入 DataSource
-
在 dao 创建两个方法:多钱和少钱的方法,在service 创建方法(转账的方法)
Spring事务管理介绍
-
事务添加到JavaEE三层结构里面 Service 层(业务逻辑层)
-
在 Spring 进行事务管理操作
有两种方式:
编程式事务管理
声明式事务管理(一般使用声明式)
-
声明式事务管理
基于注解方式
基于 xml 配置文件方式
-
在 Spring 进行声明式事务管理,底层使用 AOP 原理
Spring事务管理API
-
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
接口 PlatformTransactionManager(org.springframework.transaction) 下的
DataDourceTransactionManger(org.springframework.jdbc.datasource)
注解声明式事务管理
-
在 Spring :配置文件中配置事务管理器
<!--创建事务管理器--> <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
在 Spring 配置文件中,开启事务注解
-
在 Spring 配置文件中引入名称空间 tx
-
开启事务注解
<!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
-
在 service 类上面(获取 service 类里面方法上面)添加事务注解
-
@Transactional , 这个注解添加到类上面,也可以添加到方法上面
-
如果把这个注解添加到类上面,这个类里面所有的方法都添加事务
-
如果把这个注解添加到方法上面,为这个方法添加事务
@Service @Transactional public class UserService{ }
-
事务参数
-
propagation: 事务传播行为
当一个事务方法被另外一个事务方法调用时候,这个事务方法如何进行
-
ioslation: 事务隔离级别
- 事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
- 有三个读问题:脏读、不可重复读、虚读
- 脏读:一个未提交的事务读取到了另一个未提交事务的数据。
- 不可重复读:一个未提交的数据读到了另外一个提交事务中的数据
- 虚读:一个未提交的事务读取到了另一个未提交事务添加的数据
- 解决:通过设置事务隔离性,解决上面问题
- repeatable MySQL默认隔离级别
-
timeout : 超时时间
- 事务需要在一定时间内进行提交,如果不提交就会进行回滚
- 默认值是 -1(不超时)设置时间以秒为单位进行计算
-
readOnly :是否只读
- 读:查询操作,写:添加修改删除操作
- readOnly 默认值为 false ,表示可以查询,可以添加修改删除操作
- 设置 readOnly 值为 true,设置为 true 之后,只能查询
-
rollbackFor :回滚
- 设置查询那些异常进行事务回滚
-
noRollbackFor :不回滚
- 设置出现那些异常不进行事务的回滚
XML声明式事务管理
-
在 Spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和切面
Spring5 框架新功能
-
整个Spring5基于java8,运行时兼容 JDK9,许多不建议使用的类型和方法在代码库中删除。
-
Spring5框架自带了通用的日志封装
-
Spring5已经移除Log4jConfigListener,官方建议使用Log4j2
-
Spring5 框架整合 Log4j2
第一步:引入 jar 包
第二步:创建 Log4j2.xml配置文件
-
-
Spring5 框架核心容器支持 @Nullable 注解
- @Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性可以为空,参数值可以为空
-
Spring5 核心容器支持函数式风格 GenericApplicationContext