文章目录
- 一、Spring介绍
- 二、IOC容器(管理对象的创建)
- 1.IOC底层原理
- 2.IOC中的接口介绍(BeanFactory)
- 3.IOC操作Bean管理(概念)
- 4.Bean管理(基于xml)(重要)
- 5.Bean管理(工厂bean)
- 6.Bean管理(基于注解)(最重要)
- 7.IOC总结
- 三、AOP(增强原方法)
- 四、JdbcTemplate
- 五、事务操作
- 六、Spring5框架新功能
- 七、Spring快速总结
一、Spring介绍
1.Spring概念
-
Spring框架概述
Spring框架是一个轻量级的、开源的J2EE的框架
(轻量级:引入的jar包小、少,可以独立使用)
(框架:能够让开发更方便)
是针对bean的生命周期进行管理的轻量级容器
两个核心部分:IOC、AOP
- (IOC:控制反转,把创建对象的过程交给Spring进行管理)
- (AOP:面向切面,不修改源代码的情况下就能够进行功能的增强)
Spring框架的特点:
- 方便解耦,简化开发
- 支持AOP
- 方便程序的测试
- 方便集成各种优秀框架
- 方便进行事务操作
- 降低API开发难度(将API进行了又一次封装)
2.Spring学习内容
- IOC容器
- Aop
- JdbcTemplate
- 事务管理
- Spring5新特性
3.入门案例
-
下载Spring5
Spring官网:https://spring.io/
Spring下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/
选择对应的版本,下载dist.zip文件
-
打开IDEA,创建一个普通的java工程
-
创建一个包,导入Spring5相关的jar包
因为是基本功能,所以现在只需要导入4个jar包
导入commons-logging的jar包(Spring依赖日志)
-
创建一个普通的类,给一个方法(供Test类测试)
public class Person { public void tell(){ System.out.println("hello,Spring"); } }
-
创建Spring配置文件,在配置文件中配置需要创建的对象(普通类的)
- Spring配置文件使用xml格式
- bean标签用来创建对象
- class属性:对应bean类的路径
- id属性:给创建的对象起的别名
<?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"> <!--创建Person对象(Bean)--> <bean id="person1" class="com.junqing.test.Person"></bean> </beans>
-
进行测试代码编写
- 加载Spring配置文件
- 获取配置文件中需要创建的对象
- 调用方法
public class SpringTest_01 { public static void main(String[] args) { //1. 加载Spring配置文件 ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); //2. 获取配置文件中需要创建的对象 //填入的参数是配置文件中的id Person person1 = context.getBean("person1", Person.class);//通过反射创建对象 //3. 调用方法 person1.tell(); } }
二、IOC容器(管理对象的创建)
控制反转:把对象的创建和对象之间调用的过程,交给Spring进行管理
使用目的:降低耦合度
入门案例就是IOC的实现
1.IOC底层原理
-
使用XML解析、工厂设计模式、反射
使用工厂模式后,如果UserDao修改了类名,那么我们只需要修改工厂类里的类名(如果使用传统new方法,需要将每一个UserService里的类名都修改)
解耦原理:
2.IOC中的接口介绍(BeanFactory)
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工程
-
Spring提供IOC容器的实现两种方式(两个接口):(选择一个即可)
- BeanFactory:IOC容器基本实现,是Spring内部的使用接口(所以一般我们不使用)。加载配置文件的时候不会创建对象,在获取对象(使用)的时候才会创建对象
- ApplicationContext:BeanFactory接口的子接口,提供了更多更强大的功能,一般由开发人员使用,在加载配置文件的时候就会创建对象
-
ApplicationContext的四个实现类:
- FileSystemXmlApplicationContext:操作的文件不在工程目录下(放在电脑磁盘D盘或者C盘的xml文件用这个实现类)
- ClassPathXmlApplicationContext:操作的文件在工程目录下(放在src或者resource下的xml文件用这个实现类)
- WebApplicationContext:专门为web应用准备的(例如:Springboot的WebApplicationContext配置类)
- AnnotationConfigApplication:通过读取Java配置类创建IOC容器对象
3.IOC操作Bean管理(概念)
Bean管理指的是两个操作:
- Spring创建对象
- Spring注入属性
4.Bean管理(基于xml)(重要)
4.1、基于xml方式创建对象
<bean id="person1" class="com.junqing.test.Person"></bean>
- 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现创建对象
- 在bean标签里面有很多属性
- id属性:给对象取一个唯一别名,通过别名创建对象
- class属性:创建对象所在类的全路径
- name属性:和id一样,但是可以加特殊符号
- 创建对象的时候,默认执行无参构造器,完成对象的创建
4.2、基于xml方式注入属性
DI:依赖注入,就是注入属性
- 第一种注入方式:使用set方法进行注入
- 第二种注入方式:使用有参构造器进行注入
-
使用set方法进行注入
(1)创建类,定义属性和set方法
(2)在xml文件中配置对象
(3)在xml中使用property标签注入属性
<!--创建Person对象(Bean)--> <bean id="person1" class="com.junqing.test.Person"> <!--propert 使用set注入属性 name:类的属性名称 value:注入的值 --> <property name="name" value="yfj"></property> <property name="age" value="20"></property> </bean>
-
使用有参构造器的方法注入属性
(1)创建类,定义属性和set方法
(2)在xml文件中配置对象
(3)在xml中使用constructor-arg标签注入属性
<!--创建Person对象(Bean)--> <bean id="person1" class="com.junqing.test.Person"> <!--constructor-arg 使用构造器注入属性 name:类的属性名称 value:注入的值 --> <constructor-arg name="name" value="zyj"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> </bean>
4.3、P名称注入属性(了解即可)
可以简化xml的set注入属性方式
-
在xml中最顶上的bean标签中,添加如下信息
xmlns:p="http://www.springframework.org/schema/p"
-
这个时候后面的bean标签中就会p:属性,直接添加值即可
<bean id="person2" class="com.junqing.test.Person" p:name="zyj" p:age="20"></bean>
4.4、注入空值、特殊符号
-
注入空值null(使用null标签)(需要set方法)
<property name="address"> <null/> </property>
-
注入特殊符号(使用value标签然后用CDATA)
<property name="address"> <value><![CDATA[;;南京]]></value> </property>
4.5、注入外部bean(例如:service调用dao)
意思就是一个类调用另一个类的时候需要创建对象,这个时候怎么创建
-
创建被调用类(例如:Dao类)
-
创建调用类(Service),创建一个属性对象(是Dao类),给set方法
public class UserService { private UserDaoImpl userDao;//创建了一个属性对象 //给了set方法 public void setUserDao(UserDaoImpl userDao) { this.userDao = userDao; } }
-
在xml文件中创建两个类,调用对象添加property标签调用ref
<!--调用类Service--> <bean id="userService" class="com.junqing.test.UserService"> <!--ref 是调用类的id --> <property name="userDao" ref="userDaoImpl"></property> </bean> <!--被调用类Dao--> <bean id="userDaoImpl" class="com.junqing.test.UserDaoImpl"></bean>
4.6、注入内部bean
和外部注入Bean差不多,一个是在外部引用,一个是在里面创建
<bean id="emp" class="com.junqing.bean.Emp">
<property name="name" value="zyj"></property>
<property name="sex" value="girl"></property>
<!--内部bean-->
<property name="dept">
<bean id="dept" class="com.junqing.bean.Dept">
<!--内部bean属性注入-->
<property name="dname" value="英语部"></property>
</bean>
</property>
</bean>
4.7、注入集合属性
- 注入数组类型属性
- 注入List集合类型属性
- 注入Map集合类型属性
-
创建一个类,定义数组、List、Map、Set属性类型,生成set方法
public class Test01 { private String [] course; private List<String> list; private Map<Integer,String> map; private Set<String> set; public void setCourse(String[] course) { this.course = course; } public void setList(List<String> list) { this.list = list; } public void setMap(Map<Integer, String> map) { this.map = map; } public void setSet(Set<String> set) { this.set = set; } }
-
在Spring配置文件中进行配置
标签只给name属性,具体赋值在标签的里面
数组在里面使用,然后使用标签赋值
List在里面使用,然后使用标签赋值
Map在里面使用
Set在里面使用,然后使用标签赋值
如果存的值是对象类型的,则用替换
<bean id="test" class="com.junqing.bean.Test01"> <!--数组数据的注入--> <property name="course"> <array> <value>AJAX课程</value> <value>java30天入门</value> </array> </property> <!--List数据的注入--> <property name="list"> <list> <value>韩顺平</value> <value>王振国</value> </list> </property> <!--map数据的注入--> <property name="map"> <map> <entry key="01" value="韩顺平"></entry> <entry key="02" value="王振国"></entry> </map> </property> <!--set数据的注入--> <property name="set"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean>
4.8、提取集合注入的公共部分
-
在spring配置文件中引入名称空间
<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">
-
先使用util创建集合对象,然后外部注入
<!--先抽取出list--> <util:list id="BookList"> <value>豆奶粉</value> <value>豆沙包</value> </util:list> <!--外部注入--> <bean id="test1" class="com.junqing.bean.Test01"> <property name="list" ref="BookList"></property> </bean>
4.9、bean的作用域
- 在Spring中,可以设置创建bean实例时,是单实例还是多实例
- 单实例:getBean获取的是同一个对象
- 多实例:每次都创建一个新的对象
- 在Spring 中,默认情况下,bean是单实例
-
如何设置单实例还是多实例
-
在Spring 配置文件bean标签里面有属性(scope)用于设置
-
scope属性值:
- 默认值:singleton——表示单实例对象
- prototype——表示多实例对象
<bean id="test1" class="com.junqing.bean.Test01" scope="prototype"> </bean>
-
singleton 和 prototype 的区别
- 一个是单实例,一个是多实例
- 设置scope的值是singleton时候,加载spring配置文件的时候就会创建单实例对象
- 设置scope的值是prototype时候,在调用getBean方法的时候才会创建多实例对象
-
4.10、bean的生命周期(5步)
生命周期:对象从创建到销毁的过程
-
生命周期
-
通过构造器创建bean实例(无参构造器)
-
为bean的属性设置值和对其他bean的引用(set方法)
-
调用bean的初始化方法(需要进行配置)
给bean标签一个init-method属性,值是自己写的方法
-
bean就可以使用了(对象获取到了)
-
当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
给bean标签一个destroy-method属性,值是自己写的方法
-
-
bean生命周期的演示
-
对象类
public class Dept { private String dname;//部门名称 public Dept() { System.out.println("1.无参构造器被调用了"); } public void setDname(String dname) { this.dname = dname; System.out.println("2.set方法被调用了"); } public void init_method(){ System.out.println("3.调用了init-method"); } public void destoryMethod(){ System.out.println("4.调用了destoryMethod方法"); } @Override public String toString() { return "Dept{" + "dname='" + dname + '\'' + '}'; } }
-
Spring配置文件
<bean id="dept" class="com.junqing.bean.Dept" init-method="init_method" destroy-method="destoryMethod"> <property name="dname" value="yfj"></property> </bean>
-
测试类
public class SpringTest_01 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); Dept dept = context.getBean("dept", Dept.class); System.out.println(dept); ((ClassPathXmlApplicationContext)context).close(); } }
-
4.11、Bean生命周期(7步)[多了后置处理器]
-
生命周期
-
通过构造器创建bean实例(无参构造器)
-
为bean的属性设置值和对其他bean的引用(set方法)
-
把bean实例传递给bean后置处理器的方法
postProcessBeforeInitialization();
-
调用bean的初始化方法(需要进行配置)
给bean标签一个init-method属性,值是自己写的方法
-
把bean实例传递给bean后置处理器的方法
postProcessAfterInitialization();
-
bean就可以使用了(对象获取到了)
-
当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
给bean标签一个destroy-method属性,值是自己写的方法
-
-
实现演示
-
创建一个类,实现接口BeanPostProcessor,创建后置处理器
-
实现接口的两个方法:
postProcessBeforeInitialization(); postProcessAfterInitialization();
-
两个方法进行相关操作
-
在XML中配置后置处理器
<bean id="myBeanPost" class="com.junqing.bean.MyBeanPost"></bean>
-
其他的就是正常操作
-
4.12、自动装配(注入对象)
不需要再XML配置文件中写property标签,spring根据属性名称或者类型,自动进行属性注入
-
演示自动装配
-
bean标签属性autowire,配置自动装配:
-
autowire的两个常用值:
byName:根据属性名注入(被注入bean的id值要和类内部set方法的参数名一致)
byType:根据属性类型注入(相同类型的bean只能在xml中存在一个)
-
XML
<!--要把Dept注入emp中,Dept的id要和emp中的set方法的参数名一样--> <bean id="dept" class="com.junqing.autowire.Dept"></bean> <bean id="emp" class="com.junqing.autowire.Emp" autowire="byType"></bean>
-
测试类
public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("/com/junqing/autowire/Auto.xml"); Emp emp = context.getBean("emp", Emp.class); System.out.println(emp); } }
-
4.13、引入外部属性文件(注入属性)
使用场景:将不会被经常修改的属性和值写到properties文件中,然后引入Spring配置文件中
-
常规方法使用德鲁伊连接池
缺点:属性值是固定的,如果需要大量更改,很麻烦
-
引入数据库驱动jar包
-
引入druid的jar包
-
在Spring配置文件中进行配置
<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/book"></property> <property name="username" value="root"></property> <property name="password" value="yfj6688642"></property> </bean>
-
-
引入外部配置文件注入属性
-
创建外部属性文件(properties格式)
-
编辑外部文件
prop.driverClassName=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306:book prop.username=root prop.password=yfj6688642
-
在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="com/junqing/autowire/jdbc.properties"></context:property-placeholder>
-
将property的vale属性的值使用el表达式的形式引入
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.username}"></property> <property name="password" value="${prop.password}"></property> </bean>
-
5.Bean管理(工厂bean)
- Spring有两种类型bean:普通bean、工厂bean(FactoryBean)
- 普通bean:Spring配置文件中的class中定义的bean类型就是返回类型(class中给的是book,测试类中返回的也是book类)
- 工厂bean:Spring配置文件中的class中定义的bean类型可以和返回的类型不一样
-
创建一个类,作为工厂bean,实现接口FactoryBean(给一个反泛型)
-
实现接口中的方法,在返回的方法中定义返回的bean类型
public class MyFactory implements FactoryBean<Test01> { @Override public Test01 getObject() throws Exception { Test01 test01 = new Test01(); return test01; } @Override public Class<?> getObjectType() { return null; } }
-
在使用类中,getBean时第二个参数是泛型的类型
<bean id="myFactory" class="com.junqing.bean.MyFactory"></bean>
ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); Test01 test01 = context.getBean("myFactory", Test01.class); System.out.println(test01);
6.Bean管理(基于注解)(最重要)
6.1、什么是注解
- 注解是代码特殊标记。格式:@注解名称(属性名称=属性值, …)
- 注解可以作用在类、方法、属性上
- 使用注解目的:简化xml配置
6.2、Spring创建对象提供的注解
四个注解的功能是一样的,都可以用来创建Bean实例
- Component:通用的注解
- Service:一般用在Service层
- Controller:一般用在web层
- Repository:一般用在Dao层
6.3、基于注解创建对象(重要)
-
引入依赖(比XML方式多引入一个AOP的jar包)
-
开启组件扫描
Spring配置文件中,引入context空间
然后使用context标签开启组件扫描
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
<!--开启组件扫描 1.如果有多个包中间用”,“隔开 2.或者写包的共同上层目录 --> <context:component-scan base-package="com.junqing.autowire"></context:component-scan>
-
创建类(在类的上面添加任意一个注解)
value属性值可以省略,默认是类名(首字母小写)
value的值类似于bean的id
@Component(value = "userService") public class UserSercvice { public void say(){ System.out.println("hello,i'm Spring"); } }
-
测试使用
public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("/com/junqing/autowire/Auto.xml"); UserService userService = context.getBean("userService", UserService.class); userService.say(); } }
6.4、组件扫描配置
-
种类一:扫描指定包下所有的内容
<!--扫描junqing下面的所有包--> <context:component-scan base-package="com.junqing"></context:component-scan>
-
种类二:只扫描指定包下带注解的类
use-default-filters=“false”:表示现在不使用默认filter,使用自己配置的filter
<!--1.设置扫描的filter规则--> <context:component-scan base-package="com.junqing" use-default-filters="false"> <!--2.这个规则是:只扫描junqing包里面带Controller注解的类--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
种类三:设置指定包下哪些内容不进行扫描
<context:component-scan base-package="com.junqing.autowire" use-default-filters="false"> <!--2.设置这个包下,带Controller注解的不扫描--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
6.5、注解实现属性注入(重要)
-
Autowired:根据属性类型进行自动注入
Autowired的使用:
-
第一步:创建一个被调用类,创建一个调用类,都添加创建对象注解
//被调用类 @Controller public class User { public void say(){ System.out.println("我是User类"); } }
//测试类 @Controller public class UserService { }
-
第二步:在测试类中定义一个被调用类的属性(不需要set方法)
@Component public class UserService { private User user; }
-
第三步:添加注入属性注解
@Component public class UserService { @Autowired private User user; public void say(){ user.say(); System.out.println("UserService被调用了"); } }
-
第四步:测试类进行测试
public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("/com/junqing/autowire/Auto.xml"); //参数要和Component的value值一样,默认是类名首字母小写 UserService userService = context.getBean("userService", UserService.class); userService.say(); } }
-
-
Qualifier:根据属性名称进行注入
这个注解需要和上面的@AutoWired一起使用
说明:如果一个接口有多个实现类,使用上面的不知道是用哪个类,所以后面跟这个,然后属性值就是注入对象时给的value值
例如:
-
被调用类:添加注解
@Component(value = "user")
-
调用类:添加注解
-
@Autowired @Qualifier(value = "user")
-
-
Resource(不建议使用):可以根据类型注入,也可以根据名称注入
使用方法和@Autowried一样:如果没有给name则是按属性类型,如果给了name则是按名字
例如:
- @Resource(name = “user”)
- @Resource()
-
Value:注入普通数据类型(可以给固定值,也可以读取配置文件)
@Value(value="yfj") private String name;
6.6、完全注解开发(暂时了解即可,SpringBoot中的)
完全注解开发:连Spring的xml文件也不要,使用配置类,替换xml配置文件
-
创建相应的包,然后创建配置类
@Configuration //用来说明替代xml文件 @ComponentScan(basePackages={"com.junqing.autowire","com.junqing.bean"}) //用来说明开启扫描器 public class SpringConfig { }
-
编写各种类,使用注解注入对象、属性
-
编写测试类
public class Test { public static void main(String[] args) { //1.加载配置类 ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);//参数是配置类的类名 UserService userService = context.getBean("userService", UserService.class); userService.say(); } }
7.IOC总结
- 掌握IOC的底层原理:工厂模式、反射
- 掌握Bean管理的两种操作方式:
- 基于xml配置文件实现
- 基于注解方式实现
三、AOP(增强原方法)
1.AOP介绍
-
什么是AOP
(1)AOP:面向切面编程
(2)AOP的作用:利用AOP可以对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性
(3)通俗描述:不修改源代码,给程序增加新的功能
2.AOP底层原理(2种)
3.JDK动态代理的实现
-
JDK动态代理实现思路
-
编写JDK动态代理代码
(1)第一步:创建接口,定义方法
public interface UserService { public int add(int a,int b); public void say(); }
(2)第二步:创建接口的实现类
public class UserServiceImpl implements UserService { @Override public int add(int a, int b) { return a+b; } @Override public void say() { System.out.println("我是UserServiceImpl"); } }
(3)第三步:写InvocationHandler接口的实现类
//代理的是谁,就把谁的对象传进来 class UserServiceProxy implements InvocationHandler { //构造器里传进UserService的实现类,因为后面修改的是实现类的方法 private Object object; public UserServiceProxy(Object object){ this.object=object; } //invoke()可以给方法新增功能 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //给UserService的add方法新增功能 //1.方法执行之前 System.out.println("我给add方法新增功能了"); //2.执行原来的方法 // method.invoke方法的两个参数:第一个是传进来的对象,第二个是传进去的参数 Object invoke = method.invoke(object, args);//返回值是原方法方法的返回值 //3.方法执行之后 System.out.println("新增的方法执行完了"); return invoke; } }
(4)第三步:使用Proxy类(java.lang包下)创建接口代理对象
public class Test { public static void main(String[] args) { //接口的数组 Class [] interfaces={UserService.class}; UserService userService=new UserServiceImpl(); //返回一个接口实现类 UserService user = (UserService)Proxy.newProxyInstance(Test.class.getClassLoader(), interfaces, new UserServiceProxy(userService)); //调用方法 int add = user.add(1, 2); System.out.println(add); } }
4.AOP操作术语
-
连接点:类里面可以被增强的方法(不一定去增强)
-
切入点:实际被增强的方法
-
通知(增强):实际增强的逻辑部分(实际增加的功能)
通知类型:
- 前置通知:在被增强的方法之前执行
- 后置通知:在被增强的方法之后执行
- 环绕通知:在被增强的方法前后都执行
- 异常通知:在被增强的方法出现异常之后才会执行
- 最终通知:类似于异常种的finally,无论如何都会执行
-
切面:把通知应用到切入点的过程(把一个功能加到原功能上的过程)
5.AOP操作的准备工作
Spring框架中一般都是基于AspectJ实现AOP操作
AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring框架一起使用,完成AOP操作
基于AspectJ实现AOP操作(两种方式)
(1)基于xml配置文件实现
(2)基于注解方式实现(最常用)
-
在项目工程里面引入AOP相关依赖
-
切入点表达式
(1)切入点表达式的作用:知道哪个类里面的哪个方法进行增强
(2)语法结构:
execution([权限修饰符] [返回类型] [类的全路径] [方法名] [参数列表])
-
举例1:对com.atguigu.dao.BookDao类里面的add进行增强
execution(* com.atguigu.dao.BookDao.add(..))//访问权限省略,*表示返回类型是所有的,。。表示参数列表
-
举例2:对com.atguigu.dao.BookDao类里面的所有方法进行增强
execution(* com.atguigu.dao.BookDao.*(..))
-
举例3:对com.atguigu.dao.包里面所有类的所有方法进行增强
execution(* com.atguigu.dao.*.*(..))
-
6.AOP操作(基于注解)
-
创建类,在类里面定义方法(被增强类)
//被增强类 public class User { public void say(){ System.out.println("被增强类的方法"); } }
-
创建增强类(编写增强逻辑)
(1)增强类里面,创建方法,让不同方法代表不同通知类型
//增强类 public class UserProxy { public void before(){ System.out.println("增强类的before方法。。。"); } }
-
进行通知的配置
(1)在spring的配置文件中开启注解扫描
需要context、AOP名称空间
<context:component-scan base-package="com.junqing.aopanno"></context:component-scan>
(2)给增强类和被增强类添加创建对象的注解
@Component public class User {。。。。}
(3)在增强类上面添加注解Aspect
@Component @Aspect public class UserProxy {
(4)在spring的配置文件中开启生成代理对象
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置不同类型的通知
(1)在增强类的里面,在通知方法上面添加通知类型的注解(使用切入点表达式配置内容)
@Component @Aspect //增强类 public class UserProxy { //前置通知 @Before(value = "execution(* pack1.User.say(..))") public void before(){ System.out.println("before。。。"); } //最终通知(无论如何都会执行) @After(value = "execution(* pack1.User.say(..))") public void after(){ System.out.println("After。。。"); } //后置通知(在方法返回结果之后执行) @AfterReturning(value = "execution(* pack1.User.say(..))") public void afterReturning(){ System.out.println("afterReturning。。。"); } //异常通知(发生异常后执行,后置和环绕不会执行) @AfterThrowing(value = "execution(* pack1.User.say(..))") public void afterThrowing(){ System.out.println("afterThrowing。。。"); } //环绕通知 @Around(value = "execution(* pack1.User.say(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕之前"); proceedingJoinPoint.proceed();//表示执行被增强的方法 System.out.println("环绕之后"); } }
-
测试类中:获取配置文件,获取对象,调用方法
public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml"); User user = context.getBean("user", User.class); user.say();//直接就是被增强后的方法 } }
7.AOP操作细节(基于注解)
-
相同的切入点提取
因为每个增强方法的注解后面的value是一样的,所以可以进行抽取
//把通知的value抽取出来 @Pointcut(value ="execution(* pack1.User.say(..))") public void pointdemo(){ } //通知的value直接调用方法 //前置通知 @Before(value = "pointdemo()") public void before(){ System.out.println("before。。。"); }
-
设置增强类的优先级(如果有多个增强类对同一方法增强)
在增强类的上面添加注解**@Order(数字类型值)**,数字越小优先级越高
@Component @Aspect @Order(2)//设置优先级 public class UserProxy2 { @Before(value = "execution(* pack1.User.say(..))") public void add(){ System.out.println("UserProxy2的方法"); } }
8.AOP操作(基于XML)
了解即可
四、JdbcTemplate
Spring框架对jdbc进行封装,使用JdbcTemplate方便实现对数据库的操作
1.准备工作
-
引入相关jar包
-
在Spring配置文件配置数据库连接池信息
<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/book"></property> <property name="username" value="root"></property> <property name="password" value="yfj6688642"></property> </bean>
-
配置JdbcTemplate对象,注入DateSource
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
配置文件中开启组件扫描
-
创建一个dao接口,创建一个dao类去实现接口,然后在dao类中注入jdbcTemplate对象
public class BookImpl implements Book { @Autowired private JdbcTemplate jdbcTemplate; }
-
创建一个Serive类,在类中注入dao对象
public class BookService { @Autowired private Book book; }
2.实现数据的添加
-
写一个pojo类(对应数据库的表),生成get、set方法、构造器
-
编写dao类
-
编写添加方法(调用jdbcTemplate的update方法)
public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; //添加数据的功能 public int add(User user){ String sql="insert into user value(?,?,?)"; return jdbcTemplate.update(sql,user.getUsername(),user.getPassword(),user.getSsex()); } }
-
-
编写service类
-
编写添加方法
@Service public class UserService { @Autowired private UserDao userDao; public void add(User user){ userDao.add(user); } }
-
-
测试Service类
3.实现数据的修改和查询
和上面的添加数据方法一样修改和查询也是使用update()方法
-
修改:
@Override public int update(User user,String username) { String sql="update `user` set `username`=?,`password`=?,`ssex`=? where username=?"; return jdbcTemplate.update(sql,user.getUsername(),user.getPassword(),user.getSsex(),username); }
-
删除:
@Override public int delete(String username) { String sql="delete from `user` where `username`=?"; return jdbcTemplate.update(sql,username); }
4.实现数据的查询
-
查询返回某个值(调用queryForObject()方法)
该方法需要两个参数:一个是sql语句,另一个是数据返回类型的Class对象
@Override public int queryToone() { String sql="select count(*) from `user`"; return jdbcTemplate.queryForObject(sql,Integer.class); }
-
查询返回对象(调用queryForObject()方法)
该方法需要三个参数:
- 第一个:sql语句
- 第二个:RowMapper(是一个接口,可以对返回的不同类型数据进行封装)
- 第三个:sql语句参数的值
@Override public User queryToObject(String username) { String sql="select * from `user` where username=?"; //泛型是返回的类型,()里面是返回类型的class对象 //一定要给有参和无参构造器 return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), username); }
-
查询返回集合(调用query()方法)
调用query()方法,参数和上面一样:
- 第一个:sql语句
- 第二个:RowMapper(是一个接口,可以对返回的不同类型数据进行封装)
- 第三个:sql语句参数的值
@Override public List<User> queryAll() { String sql="select * from `user`"; List<User> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class)); return query; }
5.实现数据的批量添加(调用batchUpdate方法)
有两个参数:
- 第一个参数:sql语句
- 第二个参数:List集合(集合里面存放的是一个Object类型的数组,用来存放操作数据)
-
Dao
//需要的参数是一个list集合 //集合里面方的是一个数组 //如果放入对象,他不知道取出来的顺序 @Override public void batchAll(List <Object []> list) { String sql="insert into `user` values(?,?,?)"; int[] ints = jdbcTemplate.batchUpdate(sql, list); System.out.println(ints); }
-
测试类
List<Object[]> list=new ArrayList(); Object [] o1={"jack","asdf","男"}; Object [] o2={"Posk","kdl","女"}; list.add(o1); list.add(o2); userService.batchAdd(list);
6.实现据的批量删除和修改(调用batchUpdate方法)
和批量添加数据一样,只是sql语句不一样
-
批量删除
-
Dao
//批量删除 @Override public void batchDelete(List<Object[]> list) { String sql="delete from `user` where username=?"; int[] ints = jdbcTemplate.batchUpdate(sql, list); System.out.println(ints.toString()); }
-
Test类
List<Object[]> list=new ArrayList(); Object [] o1={"jack"}; Object [] o2={"Posk"}; list.add(o1); list.add(o2); userService.batchDelete(list);
-
-
批量修改
-
Dao类
//批量删除 @Override public void batchupdate(List<Object[]> list) { String sql="update `user` set `username`=?,`password`=?,`ssex`=? where username=?"; int[] ints = jdbcTemplate.batchUpdate(sql, list); System.out.println(ints.toString()); }
-
Test类
List<Object[]> list=new ArrayList(); Object [] o1={"wwh","208","女","杨方健"}; Object [] o2={"tut","208","女","Like"}; list.add(o1); list.add(o2); userService.batchupdate(list);
-
五、事务操作
1.什么是事务
1.1事物的概念:
事务时数据库操作最基本单元,逻辑上一组操作,要么都成功,要么都失败
1.2典型场景:
银行转账
1.3.事务的四个特性(ACID)
- 原子性
- 一致性
- 持久性
- 隔离性
2.基本事务场景(不使用spring框架)
-
创建数据库,创建表
-
编写spring配置文件
-
创建service类、dao类,pojo类,完成对象创建和注入关系
(1)service注入dao对象,dao注入jdbcTemplate对象
-
在Dao创建相关方法
- 多钱的方法
- 少钱的方法
-
在service创建转账方法(使用try-catch:)
-
测试service类方法
3.Spring事务管理介绍
-
事务添加到javaEE三层架构的Service层
-
Spring进行事务管理操作
(1)有两种方式:
- 编程式事务管理(一般不去使用,例如上面的案例)
- 声明式事务管理(spring中最长使用)
-
声明式事务管理
(1)基于注解方式(最常使用)
(2)基于xml配置文件方式
-
Spring的声明式事务管理,底层使用AOP原理
-
Spring事务管理API
提供一个接口,代表事务管理器。这个接口针对不同的框架提供不同的实现类
4.声明式事务管理(注解方式)
-
在spring配置文件中配置事务管理器
使用哪个事务管理器,就在配置文件中创建哪个事务管理器的bean
<!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
在spring配置文件中开启事务注解
(1)在Spring配置文件中引入名称空间 tx
xmlns:tx="http://www.springframework.org/schema/tx" 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中的方法上面)添加事务注解
@Transactional 这个注解可以放到类上面,也可以添加到方法上面
- 添加到类上面:这个类里面所有的方法都添加事务
- 添加到方法上面:为这个方法开启事务
@Transactional //开启事务的注解 public void change(User user1,User user2){ userDao.payMoney(user1.getMoney(),user1); userDao.getMoney(user2.getMoney(),user2); }
5.声明式事务管理(传播行为参数配置)
- propagation:事务传播行为
@Transactional(propagation = Propagation.REQUIRED)//传播行为的设置
当一个事务方法被另一个事务方法调用时候,这个事务方法如何进行
-
Required方式(事务合并):如果有事务正在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行
-
Required_NEW方式(事务独立):当前的方法必须启动新事务,并在自己的事务内运行,如果有事务正在运行,则将他挂起
-
Supports方式:如果有事务正在运行,当前的方法就在这个事务内运行,否则他可以不运行在事务中
6.声明式事务管理(隔离级别参数配置)
通过设置事务的隔离级别,达到多事务之间的操作不会产生影响的目的
有三个问题:脏读、不可重复读、虚读
- 脏读:一个未提交的事务读取到了另一个未提交事务中变化的数据
- 不可重复读:由于其他事务(提交或者未提交的事务都算)所作的修改或删除,导致同一查询,每次返回的结果不一样
- 虚读:一个未提交的事务读取到了另一个已经提交了的事务的数据
-
ioslation:事务隔离级别
@Transactional(isolation = Isolation.REPEATABLE_READ)//设置隔离级别(mysql中默认的就是:REPEATABLE_READ)
7.声明式事务管理(其他参数配置)
-
timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交则进行回滚
(2)默认值:-1(不超时),设置时间以秒为单位
@Transactional(timeout = 10)
-
readonly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readonly默认值false ,表示可以查询,可以修改添加删除数据
(3)设置readonly值是true,设置成true之后,只能查询
@Transactional(readOnly = true)
-
rollbackFor:回滚
设置出现哪些异常进行事务回滚
@Transactional(rollbackFor = RuntimeException.class)
-
noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
8.声明式事务管理(XML方式)
-
在spring配置文件中进行配置
(1)配置事务管理器
(2)配置通知
(3)配置切入点和切面
<!--开启组件扫描--> <context:component-scan base-package="com.junqing"></context:component-scan> <!--创建datesorce对象--> <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/test"></property> <property name="username" value="root"></property> <property name="password" value="yfj6688642"></property> </bean> <!--创建jdbcTemplate对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!--事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置通知--> <tx:advice id="tongzhi"> <!--配置事务参数--> <tx:attributes> <!--指定在些方法上添加事务(*代表所有)--> <tx:method name="change" isolation="READ_COMMITTED" propagation="NOT_SUPPORTED"/> <tx:method name="change*"/><!--表示所有以change开头的方法都添加事务--> </tx:attributes> </tx:advice> <!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* com.junqing.service.*(..))"/> <!--配置切面:把通知应用到切入点的过程--> <aop:advisor advice-ref="tongzhi" pointcut-ref="pt"></aop:advisor> </aop:config>
9.完全注解实现声明式事务管理
按代码的顺序来
@Configuration//用来说明替代配置文件
@ComponentScan(basePackages = "com.junqing")//用来开启组件扫描(参数是被扫描的类)
@EnableTransactionManagement//开启事务
public class ConfigSpring {
//创建数据库连接池
@Bean
public DruidDataSource getDruidSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");//数据库驱动
dataSource.setUrl("jdbc:mysql://localhost:3306/test");//数据库地址
dataSource.setUsername("root");//用户名
dataSource.setPassword("yfj6688642");//密码
return dataSource;
}
//创建jdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dateSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器对象
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
六、Spring5框架新功能
代码基于java8,同时兼容JDK9,许多不建议使用的类和方法在代码库中被删除
1.整合日志框架
- spring5已经移除了Log5jConfigListener,官方建议使用Log4j2
1.1、整合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 status="DEBUG"> <!--先定义所有的 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>
-
调用随便一个方法后,控制台会输出日志信息
2.Nullable注解
@Nullable注解可以使用在方法、属性、参数上,表示方法返回可以为空,属性可以为空,参数值可以为空
-
用在方法上表示:方法返回值可以为空
-
用在方法参数上面表示:方法参数可以为空(这里表示beanName可以不写)
-
用在属性上面表示:属性值可以为空
3.支持函数式风格GenericApplicationContext
如果自己new了一个对象,没有使用getbean方法获取,那么spring不知道这个对象的存在,因此当我们new了一个新对象之后,需要注册到IOC容器中
1.函数式风格创建对象,交给Spring进行管理
-
创建GenericApplicationContext对象
-
调用context对象的方法进行注册
-
使用context.getBean获取对象
//创建GenericApplicationContext对象 GenericApplicationContext context = new GenericApplicationContext(); //调用context对象的方法进行注册 context.refresh(); context.registerBean("user1",User.class,()->new User());//对象名,对象的Class方法,lambd表达式创建对象 //3.获取spring中刚注册的对象 User user1 = context.getBean("user1",User.class);//通过上面的对象名获取对象 System.out.println(user1);
5.整合Junit5单元测试框架
1.整合Junit4
-
创建测试类,使用注解方式
@RunWith(SpringJUnit4ClassRunner.class)//说明:单元测试框架 @ContextConfiguration("classpath:config.xml")//加载配置文件 getBean方法已经执行,可以直接注入对象 public class TestJunit4 { @Autowired private UserService userService; @Test public void test(){ User user1 = new User(); User user2 = new User(); userService.change(user1,user2);//可以直接调用方法 } }
2.整合Junit5
- 引入Junit5的jar包
-
使用两个注解完成
@ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:config.xml") public class TestJunit5 { @Autowired private UserService userService; @Test public void test(){ userService.change(); } }
-
使用一个符合注解完成上面的操作
@SpringJUnitConfig(locations = "classpath:config.xml") public class TestJunit5 { @Autowired private UserService userService; @Test public void test(){ userService.change(); } }
七、Spring快速总结
-
Spring通过工厂+反射实例化对象
-
XML方式获取bean的三种方式:
- 根据id获取bean:ApplicationContext的getBean方法只传入一个String类型的,则根据id获取bean
- 根据类型获取bean:ApplicationContext的getBean方法只传入一个Class类型的参数,则根据类型获取bean【这个要求xml中指定类型的bean只能有一个】
- 根据id和类型获取bean:ApplicationContext的getBean方法传入String类型和Class类型的参数,则根据id和类型获取bean
-
XML方式属性注入的方式
使用setter方法注入使用property标签【默认使用这个】,使用构造器注入使用constructor-arg标签注入
-
xml方式管理对象默认是单实例的,可以使用scope属性设置单实例还是多实例(singleton单实例,prototype多实例)
-
Bean的生命周期
(1):Bean对象创建【调用无参构造器】
(2):给bean对象设置相关属性
(3):Bean后置处理器【初始化之前】
(4):Bean对象初始化【调用指定初始化方法】
(5):Bean后置处理器【初始化之后】
(6):Bean对象创建完成,可以使用
(7):Bean对象销毁【可以配置自定义销毁的方法】
(8):IOC容器关闭 -
Spring中使用注解的方式:
引入依赖——xml文件中引入名称空间——开启组件扫描——添加注解
-
Spring中Bean对象的创建的注解【这几个注解的作用一样】:
Component普通组件、Respository、Service、Controller【这几个注解的value属性都可以不写,默认是类名首字母小写】
-
Spring中属性注入的注解:
@Autowired默认根据类型注入、@Autowired和@Qualifier根据名称注入、@Resource【Java自带的注解】
-
@Resource注解的说明:
是JDK中自带的,java8可以直接使用,java11以上需要导入依赖。默认根据name注入,如果没有指定name则根据属性的名字注入。如果属性名字也不对会自动根据类型注入。只能用到属性和setter方法上。
-
代理模式有两种:
静态代理【再写一个类,方法中调用被代理的方法并且添加新的业务逻辑】和动态代理。【AOP是动态代理】
-
动态代理分为两类:
JDK动态代理【用于有接口的情况,代理对象和目标对象实现同样的接口】和cglib动态代理【用于没有接口的情况,通过继承被代理对象实现】
-
AOP中的术语:
连接点【可以被增强的方法】,切入点【实际被增强的方法】,通知【增加的功能】
-
AOP的使用步骤【SpringBoot版本】:
引入aop和aspectj依赖【只是为了借用他的注解】
被代理类和代理类都加上@Component注解
代理类加上@Aspect注解
然后在代理类上编写增强方法
增强方法上加上AOP的相关注解【注解的值是切入点表达式】
-
重用切入点表达式:
有时候一个增强类里面的切入点表达式一样,那么我们可以就写一个,然后重用即可【做法:增强类里面随便创建一个方法,这个方法上面加上@Pointcut(切入点表达式),其他地方需要使用切入点表达式的时候直接写这个方法的名字就好了】【可以看苍穹外卖中的】
-
切入点表达式:
execution([权限修饰符] [返回类型] [类的全路径].[方法名].(..))
说明:【权限修饰符和返回类型共用一个*号】【…表示的是参数列表】
例如: @Before(value = "execution(* pack1.User.say(..))")
-
返回通知@AfterReturning中有一个属性returning属性,如果写上这个属性,那么可以在增强方法中获取被代理方法的返回值
-
Spring开启事务可以使用@Transactional注解
@Transactional注解有两个常用属性
-
rollabckFor
:出现什么异常进行回滚【如果不写这个属性,默认只有出现RuntimeException才会回滚】,它的值是异常类.calss//例如:设置只要出现异常就回滚 @Transactional(rollbackFor=Exception.class) public void delete(Integer id) throws Exception{ deptMapper.deleteByid(id);//删除部门 }
-
propagation
:用来设置事务的传播行为【是加入已有的事务Propagation.REQUIRED
还是再创建新的事务Propagation.Required_NEW
】【默认合并到已有的事务中】//问:什么时候使用第二种Required_NEW? //答:我们有一个需求:下单之前需要记录日志,无论是否成功,因此我们需要单独开启事务,及时下单出现异常,也会记录日志
-