Spring5.0
- 1. Spring概念
- 2. IOC容器
- 3. AOP切面
- 4. JdbcTemplate
- 5. 事务管理
1. Spring概念
- Spring是轻量级的开源的JavaEE框架
轻量级:jar比较少、比较小、不需要依赖其他组件
框架:让日常开发更简单、简化开发 - Spring可以解决企业应用开发的复杂性
- Spring的两大核心部分:IOC、AOP
- IOC:控制反转,把创建对象过程交给Spring进行管理
- AOP:面向切面,不修改源代码进行功能增强
- Spring特点
- 方便解耦,简化开发
- AOP编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低API开发(JDBC)
- Spring源码(阅读源码对往后的开发都有好处)
1.1 入门案例
1. 建立父工程
选择maven工程、next之后填写工程名称和GAV
在父工程的pom.xml在引入依赖
Spring的核心jar:Beans、Core、Context、Expression
<dependencies>
<!-- 可以和junit配合使用的jar -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- Spring核心jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<!-- 添加lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
可以在pom包中添加如下属性,避免乱码和版本问题
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<encoding>UTF-8</encoding>
<!-- versions -->
<java.version>1.8</java.version>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
</resource>
</resources>
</build>
2. 建立子工程
右击父工程新建module
在java包下新建包xxx.xxx 然后在这个报下新建entity包
实体类
public class User {
public User() {
System.out.println("初始化User....");
}
}
在resource包下新建bean.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">
<bean id="user" class="com.atguigu.spring5.entity.User"/>
</beans>
在test包下新建测试类
public class Spring5Test {
@Test
public void testUserAdd() {
// 1. 加载Spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// 2. 获取配置创建的对象
User user = context.getBean("user", User.class);
user.add();
}
}
2. IOC容器
- IOC底层原理
- IOC接口(BeanFactory)
- IOC操作Bean管理(基于xml)
- IOC操作Bean管理(基于注解)
2.1 IOC(概念和原理)
- 什么是IOC
- 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC目的:为了耦合度降低
- 入门案例就是IOC实现
- IOC底层原理
- xml解析、工厂模式、反射
- 反射:.java -> .class 反射能拿到.class文件
- 画图讲解IOC底层原理
2.2 IOC过程
- xml配置文件,配置创建的对象
- 通过service类和dao类,创建工厂类
class UserFactory {
public static UserDao getDao() {
// 1. 通过xml解析得到全路径
String classValue = "xml文件中的bean.class属性值";
// 通过反射创建对象
Class<?> aClass = Class.forName(classValue);
return (UserDao) aClass.newInstance();
}
}
2.3 IOC(接口)
- IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
- Spring提供IOC容器实现两种方式:(两个接口)
- BeanFactory: IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。加载配置文件时不会创建对象,在获取对象(使用)才去创建对象
- ApplicationContext: BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件时就会创建对象
- ApplicationContext接口有实现类
FileSystemXmlApplicationContext:系统文件路径(“D://…xxx”)
ClassPathXmlApplicationContext:src目录(“classpath://…xxx”)
2.4 IOC操作Bean管理
2.4.1 什么是Bean管理
(1) Spring创建对象
(2) Spring注入属性
2.4.2 Bean管理操作有两种方式
(1) 基于xml配置文件方式实现
(2) 基于注解方式实现
2.5 IOC操作Bean管理(基于xml方式)
2.5.1 基于xml方式创建对象
(1) 在Spring配置文件中,使用Bean标签,标签里面添加对象属性,就可以实现对象创建
(2) 在bean标签有很多属性 【id:唯一标识】 【class属性:类全路径(包类路径)】
(3) 创建对象的时候,默认是执行该类的无参构造方法
2.5.2 基于xml方式注入属性
DI:依赖注入,就是注入属性
①第一种注入方式:set方法
②第二种注入方式:构造器注入
③第三种注入方式:p名称空间注入
set方法注入
<?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">
<!-- 配置Book对象创建 -->
<bean id="book" class="com.atguigu.spring5.entity.Book" >
<property name="name" value="long"/>
</bean>
</beans>
构造器注入
<?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">
<!-- 配置Orders对象创建 -->
<bean id="orders" class="com.atguigu.spring5.entity.Orders">
<constructor-arg index="0" value="xxx"/>
<constructor-arg index="1" value="xiao"/>
</bean>
</beans>
用<constructor-arg index="0" value="xxx"/>
中的index代表有参构造的参数顺序。
p名称空间注入
(1) 添加p名称空间在配置文件中
xmlns:p="http://www.springframework.org/schema/p"
(2) 添加属性
<bean id="book" class="com.atguigu.spring5.entity.Book" p:name="九阳神功"/>
(3) 实质
p名称空间注入 实质是setter注入
2.6 IOC操作Bean管理(xml注入其他类型属性)
2.6.1 字面量
- null值
<property name="address">
<null/>
</property>
- 属性值包含特殊符号
①把<>进行转义 比如 <
②把特殊符号内容写到CDATA中
<property name="address">
<value>
<![CDATA[<<南京>>]]>
</value>
</property>
2.6.2 注入属性-外部bean
- 创建service和dao
- 在service调用dao里面的方法
- 在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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1. service和dao对象创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!-- 注入userDao对象
name属性:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.atguigu.spring5.dao.impl.UserDaoImpl"/>
</beans>
2.6.3 注入属性-内部bean
- 一对多关系:部门和员工
- 在实体类之间表示一对多关系
- 在配置文件中配置
<?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">
<bean id="employee" class="com.atguigu.spring5.bean.Employee">
<property name="name" value="小龙"/>
<property name="gender" value="男"/>
<!-- 设置对象类型属性 -->
<property name="dept">
<!-- 内部bean的方式,但是还是一般使用外部bean-->
<bean class="com.atguigu.spring5.bean.Dept">
<property name="name" value="后端组"/>
</bean>
</property>
</bean>
</beans>
2.6.4 级联赋值
<?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">
<bean id="employee" class="com.atguigu.spring5.bean.Employee">
<property name="name" value="lucy"/>
<property name="gender" value="女"/>
<property name="dept">
<bean class="com.atguigu.spring5.bean.Dept"/>
</property>
<!-- 级联赋值 -->
<property name="dept.name" value="后端组"/>
</bean>
</beans>
2.7 IOC操作Bean管理(xml注入集合属性)
- 注入数组类型属性
- 注入List
- 注入Map集合类型属性
<?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">
<!-- 集合属性的注入 -->
<bean id="student" class="com.atguigu.spring5.list.Student">
<!-- 数组类型属性注入 -->
<property name="courses">
<array>
<value>Java课程</value>
<value>数据库</value>
</array>
</property>
<!-- list类型属性注入 -->
<property name="list">
<array>
<value>n1</value>
<value>n2</value>
<value>n3</value>
<value>n4</value>
</array>
</property>
<!-- map类型属性注入 -->
<property name="maps">
<map>
<entry key="Java" value="java"/>
<entry key="PHP" value="php"/>
</map>
</property>
<!-- set类型属性注入-->
<property name="sets">
<set>
<value>s1</value>
<value>s2</value>
</set>
</property>
<!-- 注入list集合类型 值是对象 -->
<property name="courseList">
<list>
<ref bean="course1"/>
<ref bean="course2"/>
</list>
</property>
</bean>
<!-- 创建多个course对象 -->
<bean id="course1" class="com.atguigu.spring5.list.Course">
<property name="name" value="Spring5框架"/>
</bean>
<bean id="course2" class="com.atguigu.spring5.list.Course">
<property name="name" value="MyBatis框架"/>
</bean>
</beans>
2.7.1 集合部分重复利用
- 在Spring配置文件中引入名称空间
xmlns:uti="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/beans/spring-util.xsd">
- 使用util标签完成list集合注入提取
<?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: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">
<!-- 1. 提取list集合类型属性注入 -->
<util:list id="bookList">
<value>Spring</value>
<value>SpringBoot</value>
</util:list>
<!-- 2. 引用list集合 -->
<bean id="books" class="com.atguigu.spring5.list.Books" scope="prototype">
<property name="list" ref="bookList"/>
</bean>
</beans>
2.8 IOC操作Bean管理(FactoryBean)
- Spring有两种类型bean 一种普通bean 另一种工程bean(FactoryBean)
- 普通bean:在配置文件中定义bena类型就是返回类型
- 工程bean:在配置文件定义bean类型可以和返回类型不一样
①创建类,让这个类作为工厂bean实现接口FactoryBean
②实现接口里面的方法 在实现的方法中定义返回的bean类型
@Data
public class MyBean implements FactoryBean<Course> {
@Override
public Course getObject() throws Exception {
// 这部分底层是通过工厂模式反射得到的
Course course = new Course();
course.setName("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
@Test
public void myBeanTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("factorybean/bean1.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
}
2.9 IOC操作Bean管理(bean作用域)
- 在Spring里面 设置创建bean实例是单实例还是多实例
- 在Spring里面 默认情况下 bean是单实例对象
public class CollectionTest {
@Test
public void bookTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("list2.xml");
Book book1 = context.getBean("book", Book.class);
System.out.println(book1.hashCode());
Book book2 = context.getBean("book", Book.class);
System.out.println(book2.hashCode());
}
}
两者输出的hashCode()一致
3. 如何设置单实例还是多实例
(1) 在Spring配置文件bean标签里面有属性用于设置单实例还是多实例
(2) scope属性值【singleton表示单实例对象】【prototype表示多实例对象】
<bean id="book" class="com.atguigu.spring5demo2.collection.Book" scope="prototype">
<property name="list" ref="bookList"/>
</bean>
(3) singleton和prototype区别
设置scope=singleton加载Spring配置文件时就会创建单实例对象
设置scope=prototype不是在Spring配置文件时创建对象 在调用getBean方法时 创建多实例对象
2.10 IOC操作bean管理(bean生命周期)
2.10.1 生命周期
从对象创建到对象销毁的过程
2.10.2 bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean的引用(调用set方法)
- 调用bean初始化的方法(需要进行配置)
- bean可以使用了(对象获取到了)
- 当容器关闭时 调用bean的销毁的方法(需要进行配置销毁的方法)
2.10.3 演示bean声明周期
@Getter
public class OrdersLife {
private String name;
public OrdersLife() {
System.out.println("第一步 执行无参构造创建bean实例");
}
public void setName(String name) {
this.name = name;
System.out.println("第二步 调用set方法设置值");
}
/**
* 创建执行的初始化的方法
*/
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
/**
* 销毁的方法
*/
public void destroyMethod() {
System.out.println("第五步 销毁bean方法");
}
}
<?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">
<bean id="ordersLeft" class="com.atguigu.spring5.lifecycle.OrdersLife" init-method="initMethod"
destroy-method="destroyMethod">
<property name="name" value="手机"/>
</bean>
<!-- 配置后置处理器 -->
<bean id="myBeanPost" class="com.atguigu.spring5.lifecycle.MyBeanPost"/>
</beans>
测试方法
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:lifeBean.xml"})
public class LifeBeanTest {
@Autowired
private OrdersLife ordersLife;
@Test
public void ordersLeftTest() {
System.out.println("第四步 创建对象");
System.out.println(ordersLife);
}
}
2.10.4 bean的后置处理器bean生命周期有七步
在(3)之前把bean实例传递bean后置处理器的方法:postProcessBeforeInitialization()
在(3)之后把bean实例传递bean后置处理器的方法:postProcessAfterInitialization()
2.10.5 添加后置处理器
配置后置处理器之后 该xml的bean被创建实例时 会调用后置处理器的方法
2.11 IOC操作bean管理(xml自动装配)
什么是自动装配
根据指定装配规则(属性名称或者属性类型)Spring自动将匹配的属性值进行注入
2.11.1 根据属性名称自动注入
<?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">
<!-- 实现自动装配
bean标签属性 autowire 配置自动装配
byName 根据属性名称注入 注入值bean的id值和类属性名称一样
byType 根据属性类型注入
-->
<bean id="employee" class="com.atguigu.spring5demo2.autowire.Employee" autowire="byName"/>
<bean id="dept" class="com.atguigu.spring5demo2.autowire.Dept">
<property name="name" value="监管部"/>
</bean>
</beans>
2.11.2 根据属性类型自动注入
<?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">
<!-- 实现自动装配
bean标签属性 autowire 配置自动装配
byName 根据属性名称注入 注入值bean的id值和类属性名称一样
byType 根据属性类型注入
-->
<bean id="employee" class="com.atguigu.spring5demo2.autowire.Employee" autowire="byType">
</bean>
<bean id="dept" class="com.atguigu.spring5demo2.autowire.Dept">
<property name="name" value="监管部"/>
</bean>
</beans>
使用byType时xml文件不能定义相同class的bean会报错。
2.12 IOC操作Bean管理(外部管理文件)
2.12.1 直接配置数据库信息
配置druid连接池
<?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">
<!-- 直接配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/userDb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
2.12.2 引入外部属性文件配置数据库连接池
- 创建外部属性文件properties格式文件 写数据库信息
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
- 把外部properties属性文件引入到Spring配置文件中引入context命名空间
<context:property-placeholder location="classpath*:druid/jdbc.properties"/>
<?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"
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">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath*:druid/jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
</bean>
</beans>
2.13 IOC操作bean管理(基于注解方式)
2.13.1 什么是注解
- 注解是代码特殊标记 格式:@注解名称(属性名称=属性值…)
- 注解作用在类、方法、属性
- 使用注解目的:简化xml配置
2.13.2 Spring针对bean管理中创建对象提供注解
- @Component
- @Service
- @Controller
- @Repository
这四个注解的实质是一样的都可以用来创建bean实例
2.13.3 基于注解方式实现对象创建
①引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.aop.version}</version>
</dependency>
②开启组件扫描
<!-- 开启扫描器
1. 如果扫描多个包 多个包使用逗号隔开
2. 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu.spring5demo3.*"/>
③在类的上面添加注解
/**
* @description : 在注解里面value属性值可以省略不写 默认值是类名称 首字母小写
*/
@Component
public class UserService {
public void add() {
System.out.println("service add...");
}
}
④开启组件扫描细节配置
<?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"
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">
<!-- 开启扫描器
1. 如果扫描多个包 多个包使用逗号隔开
2. 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu.spring5demo3.*"/>
<!-- 示例1
use-default-filters="false" 表示 现在不使用默认filter 自己配置filter
context:include-filter 表示 设置扫描哪些内容
在 com.atguigu.spring5demo3包中只扫描 带有@Controller注解的类
-->
<context:component-scan base-package="com.atguigu.spring5demo3" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 示例2
context:exclude-filter 表示 设置不扫描哪些内容
-->
<context:component-scan base-package="com.atguigu.spring5demo3">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
2.13.4 基于注解方式实现属性注入
- @Autowire 根据属性类型进行自动装配
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add...");
userDao.add();
}
}
- @Qualifier 根据属性名称进行注入(这个注解要和@Autowired一起使用)
- @Resource 可以根据类型注入 也可以根据名称注入
- @Value 注入普通类型属性
2.13.5 完全注解开发
- 创建配置类 替代xml配置文件
/**
* @author : wenchao.long
* @date : Created in 2020/10/13 16:20 周二
* @description : 作为配置类 替代xml配置文件
*/
@Configuration
@ComponentScan(basePackages = {"com.atguigu.spring5demo3.*"})
public class SpringConfig {
}
@Configuration
类似xml中的<beans/>
@ComponentScan
扫描包
- 测试类
public class ServiceTest {
@Test
public void serviceAnnotationTest() {
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}
3. AOP切面
3.1 AOP(概念)
什么是AOP
- 面向切面编程(方面)利用AOP可以对业务逻辑的各个部分进行隔离 从而使得业务逻辑各部分之间的耦合度降低
- 通俗描述:不通过修改源代码方式 在主干功能中添加新功能
- 使用登录例子说明AOP
3.2 AOP(底层原理)
3.2.1 AOP底层使用动态代理
有两种情况动态代理
- 有接口 使用JDK动态代理
创建接口实现类代理对象 增强类的方法
- 没有接口 使用CGLIB动态代理
创建当前类子类的代理对象
3.3 AOP(JDK动态代理)
3.3.1 使用JDK动态代理 使用Proxy类里面的方法创建代理对象
java.lang.reflect.Proxy
调用newProxyInstance
方法
第一参数:类加载器
第二参数:增强方法所在的类 这个类实现的接口 支持多个接口
第三参数:实现InvocationHandler
创建代理对象 写增强的方法
3.3.2 编写JDK动态代理代码
(1) 创建接口 定义方法
(2) 创建接口实现类 实现方法
(3) 使用Proxy类创建接口代理对象
/**
* @author : wenchao.long
* @date : Created in 2020/10/14 9:31 周三
* @description :
*/
public class JdkProxy {
public static void main(String[] args) {
Class<?>[] interfaces = {UserDao.class};
UserDao userDao = new UserDaoImpl();
// 创建接口实现类代理对象
UserDao dao = (UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
System.out.println("result:" + dao.add(1, 2));
System.out.println("result:" + dao.update("143902392054393"));
}
}
/**
* 创建代理对象代码
*/
class UserDaoProxy implements InvocationHandler {
// 1. 因为创建的是UserDaoImpl的代理对象 就需要把UserDaoImpl传递过来
private final 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));
String add = "add";
String update = "update";
if (add.equals(method.getName())) {
StringBuffer sb = new StringBuffer();
for (Object arg : args) {
sb.append(arg);
}
System.out.println(sb);
}
if (update.equals(method.getName())) {
StringBuffer sb = new StringBuffer();
for (Object arg : args) {
sb.append(arg);
System.out.println(sb.reverse());
}
}
// 被增强的方法执行
Object res = method.invoke(obj, args);
// 方法之后
System.out.println("方法之后执行..." + obj);
return res;
}
}
3.4 AOP(术语)
3.4.1 连接点
类里面那些方法可以被增强 这些方法称为连接点
3.4.2 切入点
实际被真正增强的方法 称为切入点
3.4.3 通知(增强)
- 实际增强的逻辑部分称为通知(增强)
- 通知有多重类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知finally
3.4.4 切面
是动作 把通知应用到切入点过程
3.5 AOP操作(准备)
3.5.1 Spring框架一般基于AspectJ实现AOP操作
什么是AspectJ
AspectJ不是Spring组成部分 独立AOP框架 一般把AspectJ和Spring框架一起使用进行AOP操作
3.5.2 基于AspectJ实现AOP操作
- 基于xml配置文件
- 基于注解方式实现(开发一般使用注解)
3.5.3 在项目工程里面引入AOP相关依赖
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.8.RELEASE</version>
</dependency>
3.5.4 切入点表达式
- 切入点表达式作用:知道对那个类里面的那个方法进行增强
- 语法结构
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.*.*(…))
3.6 AOP操作(AspectJ注解)
3.6.1 创建类 在类里面定义方法
public class User {
public void add() {
System.out.println("add...");
}
}
3.6.2 创建增强类(编写增强逻辑)
在增强类里面 创建方法 让不同方法代表不同通知类型
public class UserProxy {
/**
* 前置通知
*/
public void before() {
System.out.println("before...");
}
}
3.6.3 进行通知的配置
①在Spring配置文件中 开启注解扫描
<context:component-scan base-package="com.atguigu.spring5demo4.*"/>
②使用注解创建User和UserProxy对象
@Component
③在增强类上面添加@Aspect
@Component
@Aspect
public class UserProxy {
/**
* 前置通知
*/
public void before() {
System.out.println("before...");
}
}
④在Spring配置文件中开启生成代理对象
<aop:aspectj-autoproxy/>
3.6.4 配置不同类型的通知
在增强类的里面 在作为通知方法上面添加通知类型注解 使用切入点表达式配置
@Component
@Aspect
public class UserProxy {
/**
* 前置通知
*/
@Before("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public void before() {
System.out.println("before...");
}
/**
* 不管有没有异常都会通知
* 最终通知
*/
@After("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public void after() {
System.out.println("after...");
}
/**
* 有异常不执行
* 后置通知(返回通知)
*/
@AfterReturning("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public void afterReturning() {
System.out.println("afterReturning...");
}
@AfterThrowing("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public void afterThrowing() {
System.out.println("afterThrowing...");
}
/**
* 环绕通知
* @param pjp pjp
* @return Object
*/
@Around("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around...之前");
pjp.proceed();
System.out.println("around...之后");
return pjp;
}
}
3.6.5 相同切入点进行抽取
@Component
@Aspect
public class UserProxy {
/**
* 相同切入点抽取
*/
@Pointcut("execution(* com.atguigu.spring5demo4.aopanno.User.add())")
public void pointcut() {
}
/**
* 前置通知
*/
@Before("pointcut()")
public void before() {
System.out.println("before...");
}
}
3.6.6 多个增强类对同一个方法进行增强,设置优先级
在增强类上面添加注解@Order(数字类型值) 数字越小优先级越高
3.7 AOP操作(AspectJ配置文件)
①创建增强类和被增强类
②在Spring配置文件中创建两个类对象
③在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">
<!-- 配置aop增强 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="point" expression="execution(* com.atguigu.spring5demo4.aopxml.Book.buy(..))"/>
<!-- 配置切面 -->
<aop:aspect ref="bookProxy">
<!-- 增强作用在具体的方法上 -->
<aop:before method="before" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
<!-- 创建对象 -->
<bean id="book" class="com.atguigu.spring5demo4.aopxml.Book"/>
<bean id="bookProxy" class="com.atguigu.spring5demo4.aopxml.BookProxy"/>
</beans>
配置文件了解,主要通过注解方法开发
3.8 完全使用注解开发
创建配置类 不需要创建xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.atguigu.spring5demo4.**"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
就相当于在xml中的开启AspectJ生成代理对象
4. JdbcTemplate
4.1 JdbcTemplate(概念和准备)
(1) 什么是JdbcTemplate
Spring框架对JDBC进行封装 使用JdbcTemplate方便实现对数据库操作
(2) 准备工作
- 引入相关jar包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
- 在Spring配置文件配置数据库连接池
- 配置JdbcTemplate对象 注入DataSource
<?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"
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">
<!-- JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入DataSource -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath*:spring5demo5/jdbc.properties"/>
<!-- 直接配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"/>
<property name="url" value="${prop.url}"/>
<property name="username" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
</bean>
</beans>
- 创建service类 创建dao类 在dao注入JdbcTemplate对象
4.2 JdbcTemplate操作数据库(添加)
4.2.1 对应数据库创建实体类
create table user_db.user
(
id bigint not null
primary key,
name varchar(100) not null,
status varchar(50) not null
);
@Data
public class User {
private String id;
private String name;
private String status;
}
4.2.2 编写service和dao
在dao进行数据库添加操作
在进行查询实体操作时jdbcTemplate.queryForObject(sql, new RowMaper<>,id);
RowMapper 是接口 返回不同类型数据 使用这个接口里面实现类完成数据封装
4.3 系列操作
@Repository
@Log
public class BookDaoImpl implements IBookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void insert(Book book) {
String sql = "insert into `book` values(?,?,?)";
int update = jdbcTemplate.update(sql, book.getId(), book.getName(), book.getStatus());
log.info("影响行数:" + update);
}
@Override
public void update(Book book) {
String sql = "update `book` set name = ?, status = ? where id = ?";
int update = jdbcTemplate.update(sql, book.getName(), book.getStatus(), book.getId());
log.info("影响行数:" + update);
}
@Override
public void delete(Long id) {
String sql = "delete from `book` where id = ?";
int update = jdbcTemplate.update(sql, id);
log.info("影响行数:" + update);
}
@Override
public int count() {
String sql = "select count(*) from `book`";
return Optional.ofNullable(jdbcTemplate.queryForObject(sql, Integer.class)).orElse(-1);
}
@Override
public Book getById(Long id) {
String sql = "select * from `book` where id = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);
}
@Override
public List<Book> list() {
String sql = "select * from `book`";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));
}
@Override
public void saveBatch(List<Book> books) {
String sql = "insert into `book` values(?,?,?)";
List<Object[]> list = new ArrayList<>();
books.forEach(c -> {
Object[] objects = new Object[3];
objects[0] = c.getId();
objects[1] = c.getName();
objects[2] = c.getStatus();
list.add(objects);
});
int[] ints = jdbcTemplate.batchUpdate(sql, list);
log.info("影响行数:" + Arrays.toString(ints));
}
@Override
public void updateBatch(List<Book> books) {
String sql = "update `book` set name = ? , status = ? where id = ?";
List<Object[]> list = new ArrayList<>();
books.forEach(c -> {
Object[] objects = new Object[3];
objects[0] = c.getName();
objects[1] = c.getStatus();
objects[2] = c.getId();
list.add(objects);
});
int[] ints = jdbcTemplate.batchUpdate(sql, list);
log.info("影响行数:" + Arrays.toString(ints));
}
@Override
public void deleteBatch(List<Long> ids) {
String sql = "delete from `book` where id = ?";
List<Object[]> list = new ArrayList<>();
ids.forEach(c -> {
Object[] objects = new Object[1];
objects[0] = c;
list.add(objects);
});
int[] ints = jdbcTemplate.batchUpdate(sql, list);
log.info("影响行数:" + Arrays.toString(ints));
}
}
5. 事务管理
5.1 事务概念
5.1.1 什么是事务
- 事务是数据库操作最基本单元 逻辑上一组操作 要么都成功 如果有一个失败所有操作都失败
- 典型场景:银行转账
5.1.2 事务四个特性(ACID)
- 原子性 - 要么都成功 要么都失败
- 一致性 - 操作之前和操作之后总量不变
- 隔离性 - 多事务操作之间不会产生影响
- 持久性 - 提交之后表中的数据发生变化
5.2 事务操作(搭建事务操作环境)
创建数据库表 添加记录
创建service搭建dao完成对象创建和注入关系
@Repository
public class UserDaoImpl implements IUserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney() {
String sql = "update account set money = money + ? where name = ?";
jdbcTemplate.update(sql, 100, "mary");
}
@Override
public void reduceMoney() {
String sql = "update account set money = money - ? where name = ?";
jdbcTemplate.update(sql, 100, "lucy");
}
}
上面代码 如果正常执行没有问题 但是如果代码执行过程中出现异常 有问题
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public void accountMoney() {
// lucy少100
userDao.reduceMoney();
// 模拟异常
int i = 10 / 0;
// mary多100
userDao.addMoney();
}
}
上面问题如何解决?
使用事务进行解决
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public void accountMoney() {
try {
// 1. 开启事务
// 2. 进行业务操作
// lucy少100
userDao.reduceMoney();
// 模拟异常
int i = 10 / 0;
// mary多100
userDao.addMoney();
// 第三步 没有发生异常 提交事务
} catch (Exception e) {
e.printStackTrace();
// 第四部 出现异常 事务回滚
}
}
}
5.3 事务操作(Spring事务管理介绍)
- 事务添加到JavaEE三层结构里面Service层(业务逻辑层)
- 在Spring进行事务管理操作
有两种方式:编程式事务管理和声明式事务管理(使用)
上述的操作过程就是编程式事务管理
一般开发不使用 因为代码会很冗余并且很麻烦 所以一般使用声明式事务管理 - 声明式事务管理
(1) 基于注解方式(使用)
(2) 基于xml配置文件方式 - 在Spring进行声明式事务管理 底层使用AOP原理
- Spring事务管理API
提供一个接口 代表事务管理器 这个接口针对不同的框架提供不同的实现类
如果是整合的是MyBatis或者使用JDBCTemplate模板、实现类就是DataSourceTransactionManager
5.4 事务操作(注解方式实现声明式事务管理)
5.4.1 在Spring配置文件中配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
5.4.2 在Spring配置文件中开启事务注解
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
5.4.3 在service类上面(或者service类里面方法上面) 添加事务注解
- @Transactional 这个注解添加到类上面 也可以添加方法上面
- 如果把这个注解添加类上面 这个类里面所有的方法都添加事务
- 如果把这个注解添加方法上面,为这个方法添加事务
注意
在开启事务注解时 如果是使用接口实现类方式。要添加<tx:annotation-driven proxy-target-class="true"/>
其中如果事务管理器id为transactionManger则transaction-manager="transactionManager"
可以省略不写
proxy-target-class
属性值决定是基于接口的还是基于类的代理被创建。
如果proxy-target-class="true"
那么基于类的代理将起作用(这时需要cglib库)
如果proxy-target-class="false"
或者这个属性被省略 那么标准的JDK 基于接口的代理将起作用
即使你未声明 proxy-target-class=“true” 但运行类没有继承接口 spring也会自动使用CGLIB代理
高版本spring自动根据运行类选择 JDK 或 CGLIB 代理
5.5 事务操作(声明式事务管理参数配置)
在Service类上面添加注解@Transactional
在这个注解里面可以配置事务相关参数
propagation: 事务传播行为
多事务方法直接进行调用 这个过程中事务是如何进行管理的
事务方法: 对数据库表数据进行变化的操作
Spring框架对于事务传播行为有7种
org/springframework/transaction/annotation/Propagation.java
项目地址Spring5.0项目