Spring5学习笔记
1、Spring 概念
- Spring 是轻量级的开源的 JavaEE 框架;
- Spring 可以解决企业应用开发的复杂性;
- Spring 有两个核心部分:IOC 和 Aop
- IOC: 控制反转,把创建对象过程交给 Spring 进行管理;
- Aop:面向切面,不修改源代码进行功能增强。
- Spring 特点:
- 方便解耦,简化开发;
- Aop 编程支持;
- 方便程序测试;
- 方便和其他框架进行整合;
- 方便进行事务操作;
- 降低 API 开发难度。
入门
-
下载Spring 选GA稳定版本;
spring-5.2.6.RELEASE-dist
-
打开idea工具,创建普通 Java工程。
-
导入 Spring5 相关 jar 包;
-
创建普通的类,创建普通方法
public class User { public void add(){ System.out.println("add...."); } }
-
创建 Spring 配置文件,在配置文件配置创建的对象
- Spring配置文件使用 xml格式;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置User对象创建--> <bean id="user" class="com.zwj.spring5.User"></bean> </beans>
-
进行测试代码编写
public class TestSpring5 { @Test public void testAdd() { //1 加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //2 获取配置创建的对象 User user = context.getBean("user", User.class); System.out.println(user); user.add(); } }
2、IOC 容器
1、IOC底层原理
1、什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理;
- 使用IOC 目的:为了耦合度降低;
- 上面的入门案例就是 IOC 实现。
2、IOC 底层原理
- xml解析、工厂模式、反射
3、图解IOC底层原理
如果要修改类路径,只需要修改配置文件即可。【为啥降低了耦合度】
2、IOC接口(BeanFactory)
-
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。
-
Spring 提供 IOC 容器实现的两种方式:(两个接口)
-
BeanFactory
:是spring内部使用的接口,不提供给开发人员进行使用;==特点:==加载配置文件的时候不会创建对象,在获取(使用)对象时才会去创建对象。
-
ApplicationContext
:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。==特点:==加载配置文件的时候就会把在配置文件里的对象进行创建。
-
-
ApplicationContext接口实现类:
File的是用于磁盘读取配置文件,Class的是用于工程内部读取的。
3、IOC 操作 Bean 管理(基于xml)
- 什么是Bean 管理
- Spring 创建对象;
- Spring 注入属性
1、基于xml方式创建对象
(1)在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建;
(2)在 bean 标签有很多属性,介绍常用的属性;
- id属性:唯一标识;
- class属性:类全路径(包类路径)
(3)创建对象时候,默认也是执行无参数构造方法完成对象创建。
2、基于 xml 方式注入属性(基础)
(1)DI:依赖注入,就是注入属性;
1、使用 set 方法进行注入
-
创建类,定义属性和对应的 set 方法;
public class Book { //创建属性 private String bname; private String bauthor; //set方法注入 public void setBname(String bname) { this.bname = bname; } public void setBauthor(String bauthor) { this.bauthor = bauthor; } }
-
在 spring 配置文件中创建对象,注入属性。
<bean id="book" class="com.zwj.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入值
-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩"></property>
</bean>
2、使用有参构造器进行注入
(1)创建类,定义属性和有参构造器;
public class Orders {
private String oname;
private String address;
public Orders(String oname, String address) {
this.oname = oname;
this.address = address;
}
}
(2)在 spring 配置文件中进行配置。
<bean id="orders" class="com.zwj.spring5.Orders">
<constructor-arg name="oname" value="abc"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
3、p 名称空间注入
(1)使用 p 名称空间注入,可以简化基于 xml 配置方式;
第一步 添加 p 名称空间在配置文件中:
第二步 进行属性注入,在bean标签里面进行操作:
<bean id="book" class="com.zwj.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏">
</bean>
3、xml注入其他类型属性
-
字面量
- null 值
<property name="address"> <null/> </property>
- 属性值包含特殊符号
<property name="address"> <value> <![CDATA[<<南京>>]]> </value> </property>
4、注入方式(内部、外部、级联)
1、注入属性-外部 bean
(1)创建两个类 service 类和 dao 类;
(2)在 service 调用 dao 里面的方法。
(3)在 spring 配置文件中进行配置
public class UserService {
//创建UserDAO类属性,生成set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service add....");
userDao.update();
}
}
<bean id="userService" class="com.zwj.spring5.service.UserService">
<!--
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDaoImp1"></property>
</bean>
<bean id="userDaoImp1" class="com.zwj.spring5.dao.UserDaoImp1"></bean>
2、注入属性-内部 bean
(1)一对多关系:部门和员工
(2)在实体类中表现一对多关系
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
}
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
}
(3)在 spring 配置文件中进行配置
<bean id="emp" class="com.zwj.spring5.bean.Emp">
<!--设置普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.zwj.spring5.bean.Dept">
<property name="dname" value="安保"></property>
</bean>
</property>
</bean>
3、注入属性-级联赋值
(1)第一种写法:
<bean id="emp" class="com.zwj.spring5.bean.Emp">
<!--设置普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.zwj.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
(2)第二种写法:需要在emp类中,定义dept的get方法。
<property name="dept.dname" value="技术部"></property>
5、xml注入集合属性
1、注入数组类型属性
2、注入 List集合类型属性
3、注入Map集合属性
(1)创建类,定义数组,list,map,set
public class Stu {
private String[] courses;
private List<String> list;
private Map<String,String> maps;
private Set<String> sets;
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setList(List<String> list) {
this.list = list;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
}
(2)spring配置
<bean id="stu" class="com.zwj.spring5.collectiontype.Stu" >
<property name="courses">
<array>
<value>java</value>
<value>数据库</value>
</array>
</property>
<property name="list">
<list>
<value>123</value>
<value>56</value>
</list>
</property>
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
</bean>
4、在集合里面设置对象类型值
<!--创建多个course对象-->
<bean id="course1" class="com.zwj.spring5.collectiontype.Course">
<property name="cname" value="spring5"></property>
</bean>
<bean id="course2" class="com.zwj.spring5.collectiontype.Course">
<property name="cname" value="mybatis"></property>
</bean>
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
5、把集合注入部分提取出来
(1)在 spring 配置文件中引入名称空间 util;
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>
(2)使用 util 标签完成 list 集合注入提取
<!--提取list集合类型属性注入-->
<util:list id="bookList">
<value>易筋经</value>
<value>九阳神功</value>
<value>九阴真经</value>
</util:list>
<!--如何使用-->
<bean id="book" class="com.zwj.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
2、FactoryBean
-
Spring 有两种类型bean,一种是普通 bean,里外一种工厂 bean(FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型;
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样。
-
第一步,创建类,让这个类作为工厂 bean,实现接口 FactoryBean;
第二步,实现接口里面的方法,在实现的方法中定义返回的 bean 类型。
public class MyBean implements FactoryBean<Course> { @Override public Course getObject() throws Exception { return null; } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return false; } }
public class TestSpring5Demo1 { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml"); Course course = context.getBean("myBean", Course.class); System.out.println(course); } }
<bean id="myBean" class="com.zwj.spring5.factorybean.MyBean"> </bean>
3、bean 作用域
-
在 Spring 里面,设置 bean 实例时单实例还是多实例;
-
在 Spring 里面,默认情况下, bean 是单实例对象;
-
如何设置单实例还是多实例:
(1)在 spring 配置文件 bean 标签里面有属性用于设置单实例还是多实例;
(2)
scope
属性值:第一个值 默认值,
singleton
,表示是单实例对象;第二个值
prototype
,表示是多实例对象。<bean id="book" class="com.zwj.spring5.collectiontype.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
(3)
singleton
和prototype
区别-
singleton
单实例,prototype
多实例; -
singleton
,加载spring 配置文件时候就会创建单实例对象;prototype
,不是在加载 spring 配置文件时候创建对象,在调用getBean
方法时候创建多实例对象。
-
4、bean生命周期
- 通过构造器创建 bean 实例(无参数构造);
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法);
- 调用 bean 的初始化的方法(需要进行配置初始化的方法);
- bean 可以使用了(对象获取到了);
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)。
如果有bean 后置处理器,生命周期变成七步,在第三步的前后都要【把 bean 实例传递 bean 后置处理器的方法】。
5、xml 自动装配
- 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。
<!--实现自动装配
bean标签属性autowire,配置自动装配;
autowire属性常用两个值;
byName根据属性名称注入,注入值bean的id值和类属性名称一样;
byType根据属性类型注入,在多个同类对象的时候,会出错
-->
<bean id="emp" class="com.zwj.spring5.autowire.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.zwj.spring5.autowire.Dept"></bean>
6、引入外部属性文件
-
直接配置数据库信息
(1)配置德鲁伊连接池;
(2)引入德鲁伊连接池依赖jar包;
<!--直接配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean>
-
引入外部属性文件配置数据库连接池
(1)创建外部配置文件
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/userDb prop.userName=root prop.password=root
(2)把外部 properties 属性文件引入到 spring 配置文件中
- 引入context 名称空间
<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 http://www.springframework.org/schema/context/spring-context.xsd">
- 在 spring 配置文件使用标签引入外部属性文件
<!--引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
4、IOC 操作 Bean 管理(基于注解)
1、优雅初体验
- 什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)
- 使用注解,注解作用在类上面,方法上面,属性上面;
- 使用注解目的:简化 xml 配置。
- Spring 针对 Bean 管理中创建对象提供注解
@Component
@Service
@Controller
@Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
- 基于注解方式实现对象创建
-
引入依赖:aop.jar
-
开启组件扫描
<!--开启组件扫描 1.如果扫描多个包,多个包使用逗号隔开 2.扫描包上层目录 --> <context:component-scan base-package="com.zwj"></context:component-scan>
-
创建类,在类上面添加创建对象注解
//在注解里面的value属性值可以省略不写 //默认值是类名称,首字母小写 @Component(value = "userService") //类似<bean id="" class=""/> public class UserService { public void add(){ System.out.println("add......"); } }
-
开启组件扫描细节配置
<!-- use-default-filters="false" 表示现在不使用默认filter,自己配置filter context:include-filter 设置扫描哪些内容 下面只扫描@Controller修饰的 --> <context:component-scan base-package="com.zwj" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- context:exclude-filter 设置哪些内容不扫描 --> <context:component-scan base-package="com.zwj"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
- 基于注解方式实现属性注入
-
@AutoWired
:根据属性类型进行自动装配;- 第一步 把service 和 Dao 对象创建,在service 和 Dao 类添加创建对象注解;
- 第二步 在service 注入 dao 对象,在 service 类添加 dao 类型;
@Service public class UserService { @Autowired private UserDao userDao; public void add(){ System.out.println("add......"); } }
-
@Qualifier
:根据属性名称进行注入;这个
@Qualifier
注解的使用,和上面@AutoWired
一起使用@Autowired //根据类型进行注入 @Qualifier(value = "userDaoImp11")//根据名称进行注入 private UserDao userDao;
-
@Resource
:可以根据类型注入,可以根据名称注入;本身不建议使用这个,因为调用的库是 javax.annotation.Resource
//@Resource //根据类型进行注入 @Resource(value = "userDaoImp11")//根据名称进行注入 private UserDao userDao;
-
@Value
:注入普通类型属性。@Value(value = "abc") private String name;
2、完全注解开发
-
创建配置类,替代xml 配置文件;
@Configuration //作为配置类,替代 xml 配置文件 @ComponentScan(basePackages = {"com.zwj"}) public class SpringConfig { }
-
编写测试类
public class testdemo1 { @Test public void testService(){ //加载配置类 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService",UserService.class); System.out.println(userService); userService.add(); } }
3、Aop
1、概念
-
什么是AOP
- 面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能;
- 使用登录例子说明 AOP
2、底层原理
-
AOP 底层使用动态代理
- 有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理
- 创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用 CGLIB动态代理
- 创建子类的代理对象,增强类的方法
3、JDK动态代理
-
使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象;
-
调用 newProxyInstance 方法
方法有三个参数:
(1)类加载器;
(2)增强方法所在的类,这个类实现的接口,支持多个接口;
(3)实现这个接口 InvocationHandler,创建代理对象,写增强的方法。
-
-
JDK 动态代理代码
(1)创建接口,定义方法;
public interface UserDao { public int add(int a,int b); public String update(String id); }
(2)创建接口实现类,实现方法
public class UserDaoImp1 implements UserDao{ @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
(3)使用 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; // } // }); UserDaoImp1 userDao = new UserDaoImp1(); 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 { //需要创建谁的代理对象,就要把谁传递过来 //有参数构造传递 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; } }
4、术语
-
连接点
类里面哪些方法可以被增强,这些方法称为连接点。
-
切入点
实际被真正增强的方法,称为切入点。
-
通知(增强)
(1)实际增强的逻辑部分称为通知(增强);
(2)通知有多种类型:
- 前置通知
@Before
- 后置通知
@AfterReturning
[返回值之后执行,有异常不执行] - 环绕通知
@Around
- 异常通知
@AfterThrowing
- 最终通知
@after
[不管有没有异常都执行]
- 前置通知
-
切面
(1)把通知应用到切入点的过程;
5、准备
-
spring 框架一般是基于 AspectJ 实现 AOP 操作
(1)什么是 AspectJ
- AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spring 框架一起使用,进行 AOP 操作
-
基于 Aspect 实现 AOP 操作
(1)基于 xml 配置文件实现;
(2)基于注解方式实现(使用)
-
在项目工程里面引入 AOP 相关依赖
-
切入点表达式
(1)切入点表达式作用,知道对哪个类里面的哪个方法进行增强;
(2)语法结构:所有用
*
替代execution([访问权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
6、AspectJ注解
-
创建类,在类里面定义方法;
public class User { public void add(){ System.out.println("add....."); } }
-
创建增强类(编写增强逻辑)
(1)在增强类里面,创建方法,让不同方法代表不同通知类型;
public class UserProxy { //前置通知 public void before(){ System.out.println("before....."); } }
-
进行通知的配置
(1)在 spring 配置文件中,开启注解扫描;
<?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" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.zwj.spring5.aopanno"></context:component-scan> </beans>
(2)使用注解创建 User 和 UserProxy 对象;
@Component public class UserProxy {
(3)在增强类上面添加注解
@Aspect
;@Component @Aspect //生成代理对象 public class UserProxy {
(4)在 spring 配置文件中开启生成代理对象。
<!--开启Aspect生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置;
@Component @Aspect //生成代理对象 public class UserProxy { //前置通知 @Before(value = "execution(* com.zwj.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before....."); } //环绕通知 @Around(value = "execution(* com.zwj.spring5.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕之前"); //被增强方法执行 proceedingJoinPoint.proceed(); System.out.println("环绕之后"); } }
-
公共切入点抽取
@Pointcut
//相同切入点抽取 @Pointcut(value = "execution(* com.zwj.spring5.aopanno.User.add(..))") public void pointdemo(){ } //前置通知 @Before(value = "pointdemo()") public void before(){ System.out.println("before....."); }
-
有多个增强类使用同一个方法进行增强,设置增强类优先级
(1)在增强类上面添加注解
@Order
(数字类型值),值越小优先级越高@Component @Aspect @Order(3) public class PersonProxy {
-
完全使用注解开发
@Configuration @ComponentScan(basePackages = {"com.zwj"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
7、AspectJ 配置文件
-
创建两个类,增强类和被增强类,创建方法;
-
在Spring 配置文件中创建两个类对象;
<!--创建对象--> <bean id="book" class="com.zwj.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.zwj.spring5.aopxml.BookProxy"></bean>
-
在 Spring 配置文件中配置切入点;
<!--配置Aop增强--> <aop:config> <!--切入点--> <aop:pointcut id="p" expression="execution(* com.zwj.spring5.aopxml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增强作用在具体方法--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
4、JdbcTemplate
1、概念和准备
-
什么是 JdbcTemplate
(1)Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作;
-
准备工作
(1)引入相关 jar包;
(2)在spring 配置文件配置数据库连接池;
(3)配置 JdbcTemplate 对象,注入 DataSource;
<!--JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"></property>
</bean>
(4)创建 service 类,创建 dao类,在 dao 注入 jdbcTemplate 对象;
- 配置文件
<!--组件扫描-->
<context:component-scan base-package="com.zwj"></context:component-scan>
- service
@Service
public class BookService {
//注入dao
@Autowired
private BookDao bookDao;
}
- dao
@Repository
public class BookDaoImp1 implements BookDao{
//注入 jdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
2、JdbcTemplate操作数据库
1、添加(修改,删除)
-
对应数据库创建相应的实体类
-
编写 service 和 dao
(1)在 dao 进行数据库添加操作
jdbcTemplate.update()
@Repository public class BookDaoImp1 implements BookDao { //注入 jdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; @Override public void add(Book book) { //sql语句 String sql = "insert into t_book values(?,?,?)"; //调用方法实现 Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } }
2、查询
1、查询表记录数
queryForObject(String sql,Class<T> requiredType)
第二个参数是返回类型Class
@Override
public int selectCount() {
String sql="select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
2、查询返回对象
queryForObject(String sql,RowMapper<T> rowMapper,Object... args)
RowMapper,是接口,返回不同类型数值,使用这个接口里面实现类完成数据封装。
@Override
public Book findBookInfo(String id) {
String sql = "select * from t_book where userId=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
3、查询返回集合
query(String sql,RowMapper<T> rowMapper,Object... args)
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
3、批量操作
1、批量添加(修改,删除)
batchUpdate(String sql,List<Object[]> batchArgs)
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
System.out.println(Arrays.toString(ints));
}
5、事务管理
1、搭建事务操作环境(模拟银行)
-
创建数据库表,添加表;
-
创建 service,搭建 dao,完成对象创建和注入关系
(1)service 注入 dao,在 dao 注入 JdbcTemplate,在JdbcTemplate 注入 DataSource;
-
在 dao 创建两个方法,多钱和少钱的方法,在 service 创建方法(转账方法)
@Service public class UserService { //注入dao @Autowired private UserDao userDao; //转账方法 public void accountMoney(){ //Lucy少100 userDao.reduceMoney(); //Mary多100 userDao.addMoney(); } }
@Repository public class UserDaoImp1 implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; @Override public void addMoney() { String sql = "update t_account set money=money+? where username=?"; jdbcTemplate.update(sql,100,"mary"); } @Override public void reduceMoney() { String sql = "update t_account set money=money-? where username=?"; jdbcTemplate.update(sql,100,"lucy"); } }
2、Spring 事务管理介绍
-
事务添加到 Service 层(业务逻辑层);
-
在Spring 进行事务管理操作:
(1)有两种方式:编程式事务管理和声明式事务管理(使用)
-
声明式事务管理
(1)基于注解方式;
(2)基于 xml 配置文件方式。
-
在Spring 进行声明式事务管理,底层使用 AOP;
-
Spring 事务管理 API
(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类;
3、注解声明式事务管理
-
在 Spring 配置文件配置事务管理器
<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
在 spring 配置文件,开启事务注解
(1) 在 spring 配置文件引入名称空间 tx
<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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
(2)开启事务注解
<!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
在 service 类上面(获取 service 类里面方法上面)添加事务注解
(1)
@Transactional
,这个注解添加到类上面,也可以添加方法上面;(2)如果把注解添加到类上面,这个类里面的所有方法都添加事务;
(3)如果把这个注解添加到方法上面,为这个方法添加事务。
@Service @Transactional public class UserService {
4、声明式事务管理参数配置
-
propagation
:事务传播行为当一个事务方法被另外一个事务方法调用时候,这个事务方法如何进行
REQUIRED
REQUIRED_NEW:分成外层事务和内层事务
-
ioslation
:事务隔离级别(1)事务有特性称为隔离性,多事务操作之间不会产生影响。
(2)有三个读问题:脏读、不可重复读、幻读
(3)通过设置隔离级别,解决这些问题。
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
-
timeout
:超时时间(1)事务需要在一定时间内进行提交,如果不提交进行回滚;
(2)默认值时-1,设置时间以秒为单位进行计算。
-
readOnly
:是否只读(1)读:查询操作;写:添加修改删除操作;
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作;
(3)设置 readOnly 值时 true,设置成 true 之后,只能查询。
-
rollbackFor
:回滚(1)设置出现哪些异常进行事务回滚;
-
noRollbackFor
:不回滚(1)设置出现哪些异常不进行事务回滚;
5、xml 声明式事务管理
-
在 spring 配置文件中进行配置;
第一步 配置事务管理器:
第二步 配置通知:
第三步 配置切入点和切面:
<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置通知--> <tx:advice id="txadvice"> <!--配置事务参数--> <tx:attributes> <!--指定哪种规则的方法上面添加事务--> <tx:method name="accountMoney" propagation="REQUIRED"/> <!-- <tx:method name="account"/>--> </tx:attributes> </tx:advice> <!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* com.zwj.spring5.service.UserService.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
6、完全注解声明式事务管理
-
创建配置类,取代xml配置文件;
@Configuration//配置类 @ComponentScan(basePackages = "com.zwj")//开启组件扫描 @EnableTransactionManagement//开启事务 public class TxConfig { //创建数据库的连接池 @Bean public DruidDataSource getDruidDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///userDb"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } //创建JdbcTemplate对象 @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource){ //到IOC容器中根据类型找到dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); //注入dataSource jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } //创建事务管理器对象 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } }
6、Spring5 新特性
整个 Spring5 框架的代码基于Java8,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中删除;
1、Spring5 框架自带了通用的日志封装;
(1)Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2;
(2)Spring5 框架整合 Log4j2;
第一步 引入jar包
第二步 创建 log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--日志级别以及优先级排序:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration stayus="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
手动测试日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserLog {
private static final Logger log = LoggerFactory.getLogger(UserLog.class);
public static void main(String[] args) {
log.info("hello log4j2");
}
}
2、Spring5 框架核心容器支持@Nullable注解
-
@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空;
@Nullable String getId(); public void registerBean(@Nullable String beanName){} @Nullable private String bookName;
3、Spring5核心容器支持函数式风格GenericApplicationContext
//函数式风格创建对象,交给Spring进行管理
@Test
public void testGenericApplicationContext() {
//创建GenericApplicationContext
GenericApplicationContext context = new GenericApplicationContext();
//调用context的方法对象注册
context.refresh();
context.registerBean("user1",User.class, () -> new User());
//获取在spring注册的对象
User user = (User)context.getBean("user1");
System.out.println(user);
}
4、Spring5支持整合 JUnit5
(1)整合 JUnit4
第一步 引入Spring 相关针对测试依赖
spring-test-5.2.6.RELEASE.jar
第二步 创建测试类,使用注解方式完成
@RunWith(SpringJUnit4ClassRunner.class)//指定单元测试的版本
@ContextConfiguration("classpath:bean1.xml")//加载测试文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.accountMoney();
}
}
(2)Spring5整合 JUnit5
第一步 引入 JUnit5 的 jar 包
第二步 创建测试类,使用注解方式完成
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}
(3)使用一个复合注解替代上面两个注解
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}