1. Spring
1.1 简介
- 2002,首次推出了Spring框架的雏形:interface21框架!
- Spring框架是以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年发布了1.0正式版。
- Rod Johnson,Spring Framework创始人,著名作者。悉尼大学音乐学博士
- Spring理念:使现有的技术更加容易使用,整合了现有的技术框架,本身是一个大杂烩
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.2 优点
- Spring是一个开源的免费的框架
- Spring是一个轻量级、非入侵式的框架
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP) 的框架
1.3 组成
1.4 扩展
Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
Spring Cloud
- SpringCloud是基于SpringBoot实现的
弊端:发展太久之后,违背了原来的理念!配置十分繁琐,人称配置地狱
2. IOC理论推导
之前的Service层调Dao层的时候,需要在Service层实现类创建一个Dao层实现类的对象,但是如果Dao层实现类增多,这样就需要我们频繁修改Service层实现类,不好。
但是如果做了以上修改,写成一个set方法,这样就可以自己创建任意一个Dao层的实现类,从而实现不同的功能。
之前,程序是主动创建对象的,控制权在程序员手中。
使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象。
这种思想,从本质上解决了问题,程序员不用再去管理对象的创建了,系统的耦合性大大降低,可以更加专注于业务的实现。这是IOC的原型。
IOC本质:
控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是lOC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3. 第一个Spring程序
1.创建一个实体类User
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
2.创建beans.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">
<!--使用Spring来创建对象,在Spring中这些都称为Bean
类型名 变量名 = new 类型名();
User user = new User();
id = 变量名
class = 类型名
property 给对象的属性赋值 这个过程就是注入过程
-->
<bean id="user" class="com.hua.pojo.User"> <!--相当于 User user = new User(); -->
<property name="name" value="张三"></property> <!--相当于 user.setName = "张三" -->
</bean>
</beans>
3.测试
public class MyTest {
public static void main(String[] args) {
// 获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象都在Spring中管理,使用时直接从里面取出来
Hello hello =(Hello) context.getBean("hello");
System.out.println(hello);
}
}
控制:谁来控制对象的创建,传统的程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:利用set方法来进行注入,实体类中得有set方法
IOC:是一种思想,是由主动的编程变成被动的接收
所谓IOC就是对象由Spring创建、装配、管理
修改上述案例
// UserDao
public interface UserDao {
void getUser();
}
//UserDaoImpl
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据");
}
}
//MySqlDaoImpl
public class MySqlDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
// UserService
public interface UserService {
void getUser();
}
//UserServiceImpl
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
<?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="mysqlImpl" class="com.hua.dao.MySqlDaoImpl"></bean>
<bean id="userImpl" class="com.hua.dao.UserDaoImpl"></bean>
<!--ref是引用Spring托管的类 class是普通类-->
<bean id="userServiceImpl" class="com.hua.service.UserServiceImpl">
<property name="userDao" ref="userImpl"></property>
</bean>
</beans>
public class myTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
userServiceImpl.getUser();
}
}
4. IOC创建对象
1.使用无参构造创建对象 默认的
2.使用有参构造创建对象
public User(String name){
this.name = name;
System.out.println("User的有参构造");
}
第一种方式:通过下标赋值
<bean id="user" class="com.hua.pojo.User">
<constructor-arg index="0" value="张三"/>
</bean>
第二种方式:通过类型赋值,但当有多个值是同一种类型的时候,该方法不能使用
<bean id="user" class="com.hua.pojo.User">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
第三种方式:通过参数名 常用
<bean id="user" class="com.hua.pojo.User">
<constructor-arg name="name" value="李四"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
5. Spring配置
5.1 别名
alias
<bean id="user" class="com.hua.pojo.User">
<constructor-arg name="name" value="李四"></constructor-arg>
</bean>
<alias name="user" alias="user2"></alias>
5.2 Bean的配置
<!-- id: bean的唯一标识符
class:bean对象所对应的全限定名:包名+类名
name: 也是别名,可以同时取多个
-->
<bean id="user" class="com.hua.pojo.User" name="user1">
<constructor-arg name="name" value="李四"></constructor-arg>
</bean>
5.3 import
一般用于团队开发使用,可以将多个配置文件导入合并为一个配置文件。
假设有团队中有三个人开发不同的类,不同的类注册在不同的bean中,只需使用import将所有的beans.xml合并为一个即可。
6. 依赖注入(属性赋值)
依赖注入说白了就是创建对象的时候给属性赋值,常见的有两种:
一种是使用构造器 User user = new User(101,“张三”,12) 对应下方的构造器注入,要求实体类中得有构造器才行
另一种是使用set方法,User user = new User(); user.setId=101;user.setName=“张三”;user.setAge(12);对应下方的set方式注入,要求得有set方法
6.1 构造器注入
上述。
6.2 Set方式注入*
- 依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器
- 注入:创建的bean对象中的所有属性,由容器来注入
address类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String address;
}
Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
}
beans.xml
<bean id="address" class="com.hua.pojo.Address">
<property name="address" value="北京"></property>
</bean>
<bean id="student" class="com.hua.pojo.Student">
<!--第一种方式,普通值注入 value -->
<property name="name" value="张三"></property>
<!--第二种方式,bean注入 ref -->
<property name="address" ref="address"></property>
<!--数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--List-->
<property name="hobbies">
<list>
<value>听歌</value>
<value>玩游戏</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="身份证" value="1*******************8"></entry>
<entry key="银行卡" value="2*******************5"></entry>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>和平精英</value>
<value>王者荣耀</value>
</set>
</property>
<!--null-->
<property name="wife">
<null></null>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/test</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
test
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student);
}
}
6.3 扩展方式注入
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,直接注入属性的值 相当于property-->
<bean id="user" class="com.hua.pojo.User" p:name="张三" p:age="18"></bean>
<!--c命名空间注入,通过构造器注入 -->
<bean id="user1" class="com.hua.pojo.User" c:age="18" c:name="李四"></bean>
</beans>
可以使用p命名空间和c命名空间进行注入,但是不能直接使用,需要导入xml约束
xmlns:p=“http://www.springframework.org/schema/p”
xmlns:c=“http://www.springframework.org/schema/c”
6.4 bean的作用域
1.单例模式(Spring默认机制)
<bean id="user" class="com.hua.pojo.User" p:name="张三" p:age="18" scope="singleton"></bean>
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1==user2); // 输出为true
2.原型模式:每次从容器中get时,都会产生一个新对象
<bean id="user" class="com.hua.pojo.User" p:name="张三" p:age="18" scope="prototype"></bean>
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1==user2); // 输出为false
3.request、session、application 只能在web开发中使用。
7. 自动装配(属性自动赋值)
自动装配是Spring满足bean依赖的一种方式,Spring会在上下文中自动寻找,并自动给bean自动装配属性
说白了自动装配就是通过属性的名字或者类型自动赋值的过程
在Spring中有三种装配方式:
1.在xml中显式地配置
2.在java中显式地配置
3.隐式地自动装配
7.1 测试
三个类:Cat、Dog、People
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
private Dog dog;
private Cat cat;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
private String name;
private int age;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Cat {
private String name;
private int age;
}
显式的配置
<?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.hua.pojo.User" name="user2">
<property name="id" value="101"></property>
<property name="name" value="张三"></property>
<property name="age" value="12"></property>
<property name="dog" ref="dog"></property>
<property name="cat" ref="cat"></property>
</bean>
<bean id="dog" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="cat" class="com.hua.pojo.Cat">
<property name="name" value="小咪"></property>
<property name="age" value="3"></property>
</bean>
</beans>
7.2 byName自动装配
<bean id="user" class="com.hua.pojo.User" name="user2" autowire="byName">
<property name="id" value="101"></property>
<property name="name" value="张三"></property>
<property name="age" value="12"></property>
</bean>
<bean id="dog" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="cat" class="com.hua.pojo.Cat">
<property name="name" value="小咪"></property>
<property name="age" value="3"></property>
</bean>
autowire=“byName”:在容器中查找与set方法后面的字符串相同id的bean 进行自动装配 setCat1() id = “cat1”
7.3 byType自动装配
<bean id="user" class="com.hua.pojo.User" name="user2" autowire="byType">
<property name="id" value="101"></property>
<property name="name" value="张三"></property>
<property name="age" value="12"></property>
</bean>
<bean id="dog" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="cat" class="com.hua.pojo.Cat">
<property name="name" value="小咪"></property>
<property name="age" value="3"></property>
</bean>
autowire=“byType”:在容器中查找与对象属性类型相同的bean
小结:
- byName需要保证所有的bean的id唯一,并且bean的id要与自动注入的属性的set方法后的字符串一致。此处set后的字符串指的是set方法中set后的字符串且首字母要小写,即setName id=“name” setAge111 id=“age111”
- byType需要保证类型唯一,并且bean的类型要与自动注入的属性的类型一致。
7.4 使用注解自动装配
1.导入约束 context
2.配置注解的支持 context:annotation-config/
<?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:annotation-config/>
<bean id="user" class="com.hua.pojo.User" name="user2">
<property name="id" value="101"></property>
<property name="name" value="张三"></property>
<property name="age" value="12"></property>
</bean>
<bean id="dog" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="cat" class="com.hua.pojo.Cat">
<property name="name" value="小咪"></property>
<property name="age" value="3"></property>
</bean>
</beans>
@Autowired:直接在属性上使用即可,也可以在set方法上使用,使用Autowired 可以不用写set方法,前提是自动装配的属性在IOC容器中存在,且符合名字要求
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
@Autowired
private Dog dog; // 意思就是IOC容器中已经定义好了一个dog对象,将该对象赋值给User的属性dog
@Autowired
private Cat cat; // 意思就是IOC容器中已经定义好了一个cat对象,将该对象赋值给User的属性cat
}
补充:如果有以下情况,则表示这个字段可以为null,另注解**@Nullable**也有一样的功能
@Autowired(required = false)
private Dog dog;
如果自动装配的环境复杂,自动装配无法通过一个注解@Autowired完成的时候,可以使用@Qualifier(“id名”)配合@Autowired使用
<bean id="dog1" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="dog2" class="com.hua.pojo.Dog">
<property name="name" value="大黄"></property>
<property name="age" value="4"></property>
</bean>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
@Autowired // 此处@Autowired是先通过类型匹配,发现有两个dog对象,dog1和dog2,之后通过名字进行匹配,发现都不行,报错
private Dog dog;
@Autowired
private Cat cat;
}
此时就需要加上@Qualifier()
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
@Autowired
@Qualifier("dog2") // 指明是赋值为dog2对象
private Dog dog;
@Autowired
private Cat cat;
}
@Resource
Resource注解会先找同名的,如果没有同名的就找同类型的,如果没有同类型或者有多个同类型的就会报错
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
@Resource(name = "dog1")
private Dog dog;
@Autowired
private Cat cat;
}
<bean id="user" class="com.hua.pojo.User" name="user2">
<property name="id" value="101"></property>
<property name="name" value="张三"></property>
<property name="age" value="12"></property>
</bean>
<bean id="dog1" class="com.hua.pojo.Dog">
<property name="name" value="旺财"></property>
<property name="age" value="2"></property>
</bean>
<bean id="dog2" class="com.hua.pojo.Dog">
<property name="name" value="大黄"></property>
<property name="age" value="4"></property>
</bean>
小结:
Autowired和Resource区别:
- 都是用来进行自动装配的,都可以放在属性字段上
- @Autowired默认通过byType的方式实现,找不到类型则通过byName实现,如果都找不到,则报错
- @Resource默认通过byName的方式实现,找不到名字则通过byType实现,如果都找不到,则报错
- 执行顺序不同:@Autowired默认通过byType的方式实现 @Resource默认通过byName的方式实现
总之:使用@Autowired时,先匹配类型,再匹配名字,如果有多个相同类,则结合@Qualifier(“”),使用@Resource时,先匹配名字,再匹配类型,如果有多个相同类,则结合@Resource(name=“”)。
8. 注解开发
在Spring4之后,要使用注解开发,必须保证导入aop的包
使用注解需要导入context约束,增加注解的支持
<?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:component-scan base-package="com.hua.pojo"/>
<context:annotation-config/>
</beans>
1.bean
2.如何注入
// @Component 等价与注册了个bean <bean id="user" class="com.hua.pojo.User"></bean> 就是创建了一个对象
@Component
public class User {
// 相当于 <property name="name" value="张三"/> 就是给属性赋值
@Value("张三")
public String name;
}
3.衍生的注解
@Component有几个衍生的注解,在web开发中,会按照MVC三层架构分层,这四个功能是一样的,都是将某个类注册到Spring中,装配bean
- dao【@Repository】
- service【@Service】
- controller 【@Controller】
4.自动装配
@Autowired:自动装配,先通过类型,后通过名字,如果都不行可通过@Qualifier(“”)
@Resource:自动装配,先通过名字,后通过类型,如果不行可通过@Resource(name=“”)
@Nullable:如果字段有这个注解,表示这个字段可为空
@Autowired(required = false) 也可表示该字段可为空
5.作用域
@Scope(“prototype”)
@Scope(“singleton”)
6.小结
xml与注解:
- xml更加万能,适合于任何功能,维护简单方便
- 注解 不是自己的类不能使用,维护相对复杂
最佳实践:xml用来管理bean 注解负责属性的注入
使用注解,必须要注解生效,需要开启注解的支持
<!--执行要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.hua.pojo"/>
<!--打开注解支持-->
<context:annotation-config/>
9. Java方式配置Spring
JavaConfig是Spring的子项目,在Spring4之后成为核心功能,之后经常需要写配置类
1.创建一个实体类User
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component // 使用Component代表在容器中创建一个bean 就是创建一个对象
public class User {
@Value("张三") // 给对象赋值
private String name;
}
2.编写配置类 MyConfig
// @Configuration代表这是一个配置类,相当于一个beans.xml 就是一个IOC容器
@Configuration
@ComponentScan("com.hua.pojo")
@Import(MyConfig2.class) // 引入另一个配置类
public class MyConfig {
@Bean // 注册一个bean,<bean id="user1" class="User"></bean> 其实就是创建了一个对象
public User user1(){
return new User(); // 返回注入到bean的对象
}
}
// 这样就可以其他地方使用
@Autowired
User user1;
// 自动注入,直接使用该对象
10. 代理模式
为什么要代理模式?这是SpringAop的底层
代理模式分类:
- 静态代理
- 动态代理
10.1 静态代理
角色分析:
- 抽象角色:一般使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
步骤:
1.接口
2.真实角色
3.代理角色
4.客户端访问代理角色
租房实例
1.出租房这件事情 使用接口实现 Rent
package com.hua.demo01;
// 租房
public interface Rent {
public void rent();
}
2.房东
// 房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
3.中介
package com.hua.demo01;
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
seeHouse();
hetong();
free();
}
public void seeHouse(){
System.out.println("中介带你看房子");
}
public void hetong(){
System.out.println("签合同");
}
public void free(){
System.out.println("收中介费");
}
}
4.租房子的人
package com.hua.demo01;
public class Client {
public static void main(String[] args) {
// 房东要出租房子
Host host = new Host();
// 代理,帮房东出租房子,但是代理会有一些附属操作
Proxy proxy = new Proxy(host);
// 找中介租房
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不再去关注一些公共业务
- 公共业务交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍
10.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 jdk动态代理
- 基于类 cglib
- java字节码实现 javasist
了解两个类:Proxy(代理),InvocationHandler(调用处理程序)
// 会用这个类动态生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target){
this.target = target;
}
//生成得到的代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override // 处理代理实例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
public class Client {
public static void main(String[] args) {
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色 不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
动态代理好处:
- 可以使真实角色的操作更加纯粹,不再去关注一些公共业务
- 公共业务交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般对应的是一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
11. AOP
配置中四要素:切面(可不要)、切入点、通知的类型、将通知与切入点联系起来
有三种实现方式:1. 使用Spring原生的API 2. 使用自己创建的通知类 3. 使用注解
11.1 什么是AOP
AOP,面向切面编程,通过预编译方式和运行期间动态代理实现程序功能统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要的内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2 AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
1.AOP即面向切面编程。
2.作用:在程序运行期间,不修改源码就可以对已有方法进行增强。(动态代理的作用)
3.优势:减少重复代码,提高开发效率,维护方便。
4.AOP相关术语
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。
-
Pointcut(切入点): 所谓切入点是指我们要对那些Joinpoint进行拦截的定义,所有连接点的集合。
-
advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情。
-
通知类型:前置通知,后置通知,异常通知,最终通知,环绕通知
-
Target(目标对象):代理的目标对象。
-
Weaving(织入):是指把增强添加到目标对象来创建新的代理对象的过程。
-
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
-
Aspect(切面):是切入点和通知的集合。
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将需要增强的类注册到IOC容器-->
<bean id="userService" class="com.hua.service.UserServiceImpl"></bean>
<!-- spring中基于xml的Aop的配置步骤
1.把通知Bean注册到IOC容器
2.使用aop:config标签表明开始AOP管理
3.使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的id
4.在aop:aspect标签的内部使用对应标签来配置通知类型
aop:before:表示配置前置通知
method属性:用于指定通知类那个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中那些方法进行增强
切入点表达式的写法
关键字:execution(表达式)
表达式: 访问修饰符 返回值 包名.类名.方法名(参数列表)
标准的表达式写法
public void com.hua.service.UserServiceImpl.saveUser();
访问修饰符可以省略
void void com.hua.service.UserServiceImpl.saveUser();
返回值也可以使用通配符,表示任意返回值
* com.hua.service.UserServiceImpl.saveUser();
包名可以使用通配符,表示任意返回值。单有几级包,就需要写几个*.
* *.*.*.*.UserServiceImpl.saveUser();
包名可以使用..表示当前包及其子包
* *..UserServiceImpl.saveUser()
类名和方法名也可以使用*来实现通配
* *..*.*()
参数列表:
可以直接写数据类型:
基本类型直接写名称:int
应用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但必须有参数
可以使用..表示有无参数均可,有参数可以是任意类型
全通配写法:
* *..*.*(..)
实际开发中切入点表达式的通常写法
切到业务层的包下的所有类的所有方法,参数不限
* com.hua.service.*.*(..)
通知类型
aop:before 前置通知:在切入点的方法执行之前执行
aop:after-returning 后置通知:在切入点方法正常执行之后执行
aop:after-throwing 异常通知:在切点方法执行产生异常之后执行
aop:after 最终通知:无论切入点方法是否正常执行他都会在其后面执行
aop:around 环绕通知:可以在目标执行全过程中执行
配置切入点表达式 id属性用于指定表达式的唯一标识,expression属性用于指定表达式内容
此标签写在aop:aspect标签内部只能当前切面使用
它还可以写在aop:aspect外面,此时就变成了所有切面可以使用
aop:pointcut id ="" expression="execution()"
-->
<bean id="logger" class="com.hua.utils.Logger"></bean>
<aop:config>
<aop:aspect id="beforelogger" ref="logger" >
<aop:before method="printlogger" pointcut="execution(* com.hua.service.*.*(..))">
</aop:before>
</aop:aspect>
</aop:config>
</beans>
11.3 使用Spring实现AOP
导入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
方式一:使用原生Spring API接口
// 定义一个前置通知
public class Before implements MethodBeforeAdvice {
// method:要执行的目标对象的方法
// args:参数
// target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法"+",当前参数为"+objectMapper.writeValueAsString(args));
}
}
// 定义一个后置通知
public class After implements AfterReturningAdvice {
// returnValue 返回的对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("执行的结果为"+objectMapper.writeValueAsString(returnValue));
}
}
public interface UserService {
void add();
void delete();
void update();
void select();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void select() {
System.out.println("查询了一个用户");
}
}
applicationContext.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置bean-->
<bean id="before" class="com.hua.aop.before"></bean>
<bean id="after" class="com.hua.aop.after"></bean>
<bean id="UserServiceImpl" class="com.hua.service.UserServiceImpl"></bean>
<!--配置AOP:需要导入aop的约束-->
<aop:config>
<!--切入点:expression:表达式,execution(要执行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.hua.service.UserServiceImpl.*(..))"/>
<!--可以理解为一个特殊的切面,将切入点与通知联系起来-->
<aop:advisor advice-ref="before" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
测试:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理代理的是接口
UserService userServiceImpl = (UserService) context.getBean("userServiceImpl");
userServiceImpl.add();
}
}
方式二:使用自定义类
public class MyAdvice {
public void before(JoinPoint joinPoint) throws JsonProcessingException {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("执行了"+className+"类的"+methodName+"方法,参数为"+objectMapper.writeValueAsString(args));
}
public void after(JoinPoint joinPoint){
System.out.println("方法执行之后");
}
}
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="UserServiceImpl" class="com.hua.service.UserServiceImpl"></bean>
<bean id="MyAdvice" class="com.hua.aop.MyAdvice"></bean>
<aop:config>
<aop:aspect ref="MyAdvice"> <!--引入自定义的通知-->
<aop:pointcut id="pointCut" expression="execution(* com.hua.service.*.*(..))"/>
<aop:after method="after" pointcut-ref="pointCut"></aop:after>
<aop:before method="before" pointcut-ref="pointCut"></aop:before>
</aop:aspect>
</aop:config>
</beans>
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userServiceImpl");
userService.add();
}
}
方式三:使用注解实现
@Aspect // 切面
public class MyAspect {
@Before("execution(* com.hua.service.*.*(..))")
public void before(JoinPoint joinPoint) throws JsonProcessingException {
System.out.println("方法执行之前");
}
-------------------------------------------------------------------------------------------------------------------
@After("execution(* com.hua.service.*.*(..))")
public void after(JoinPoint joinPoint){
System.out.println("方法执行之后");
}
-------------------------------------------------------------------------------------------------------------------
@Around("execution(* com.hua.service.*.*(..))") // 比after和before先执行
public void around(ProceedingJoinPoint pjp) throws Throwable {
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("执行了"+className+"类的"+methodName+"方法,参数为"+objectMapper.writeValueAsString(args));
Object result = pjp.proceed();
System.out.println("执行结果为"+objectMapper.writeValueAsString(result));
}
}
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="UserServiceImpl" class="com.hua.service.UserServiceImpl"></bean>
<bean id="MyAspect" class="com.hua.aop.MyAspect"></bean>
<aop:aspectj-autoproxy/>
</beans>
11.4 使用SpringBoot实现AOP
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
controller
@RestController
public class TestController {
@RequestMapping("/test")
public String test1(String name){
return "hello,"+name;
}
}
@Aspect // 切面
@Component
public class MyAspect {
private Logger logger = LoggerFactory.getLogger(MyAspect.class);
// 通知类型 切入点
@Around("execution(* com.hua.controller.*.*(..))")
public Object MyAdvice(ProceedingJoinPoint pjp) throws Throwable { // 通知
String className = pjp.getTarget().getClass().toString(); // 当前类的名字
String methodName = pjp.getSignature().getName(); // 当前方法的名字
Object[] args = pjp.getArgs(); // 当前参数
ObjectMapper objectMapper = new ObjectMapper();
// 方法执行之前日志输出执行的什么类的什么方法以及使用的参数
logger.info("执行了"+className+"的"+methodName+"方法"+",当前参数为"+objectMapper.writeValueAsString(args));
Object result = pjp.proceed(); // 切入点的方法执行
// 方法执行之后,日志输出结果
logger.info("执行的结果为"+result.toString());
return result;
}
}
// : 执行了class com.hua.controller.TestController的test1方法,当前参数为["abc"]
// : 执行的结果为hello,abc
12. 整合MyBatis
步骤:
1.导入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.15</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
2.编写配置文件
3.测试
12.1 回顾MyBatis
编写工具类
编写核心配置文件
编写dao
编写mapper.xml
测试
12.2 整合方式一
1.编写数据源配置
2.sqlSessionFactory
3.sqlSessionTemplate
spring-dao.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/hua/dao/UserMapper.xml"/>
<property name="configLocation" value="classpath:MybatisConfig.xml"></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
MybatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.hua.pojo"/>
</typeAliases>
</configuration>
4.dao
public interface UserMapper {
List<User> getList();
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hua.dao.UserMapper">
<select id="getList" resultType="user">
select * from user
</select>
</mapper>
service
public interface UserService {
List<User> getList();
}
UserServiceImpl
public class UserServiceImpl implements UserService{
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<User> getList() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
return mapper.getList();
}
}
5.将实现类注入到Spring中,测试
spring-service.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">
<import resource="spring-dao.xml"/>
<bean id="userMapperImpl" class="com.hua.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"></property>
</bean>
</beans>
application.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<import resource="spring-service.xml"/>
</beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService)context.getBean("userService");
userService.getList();
}
12.3 整合方式二
1.数据源
2.SqlSessionFactory
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/hua/dao/UserMapper.xml"/>
<property name="configLocation" value="classpath:MybatisConfig.xml"></property>
</bean>
3.实体类
public class UserServiceImpl1 extends SqlSessionDaoSupport implements UserService{
@Override
public List<User> getList() {
return getSqlSession().getMapper(UserMapper.class).getList();
}
}
4.注册
<import resource="spring-dao.xml"/>
<bean class="com.hua.service.UserServiceImpl1" id="serviceImpl1">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
5.测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService)context.getBean("serviceImpl1");
userService.getList();
}
13. 声明式事务
13.1 回顾事务
- 要么都成功,要么都失败
- 十分重要,涉及到数据一致性问题
事物的ACID:
原子性:
一致性:
隔离性:
持久性:
13.2 Spring中的事务管理
- 声明式事务:AOP的应用 (推荐)
- 编程式事务:需要在代码中进行事务管理
public interface UserMapper {
List<User> getList();
int add(User user);
int delete(@Param("id") int id);
}
<mapper namespace="com.hua.dao.UserMapper">
<select id="getList" resultType="user">
select * from user
</select>
<insert id="add" parameterType="user">
insert into user(id,name,age) values (#{id},#{name},#{age})
</insert>
<delete id="delete" parameterType="_int">
deletes from user where id = #{id}
</delete>
</mapper>
public interface UserService {
List<User> getList();
int add(User user);
int delete(int id);
}
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUserList() {
User user = new User(20, "小李", "12300");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(15);
return mapper.getUserList();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
application.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<import resource="spring-service.xml"/>
</beans>
MybatisConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.hua.pojo"/>
</typeAliases>
</configuration>
spring-dao.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"
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/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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/hua/dao/UserMapper.xml"/>
<property name="configLocation" value="classpath:MybatisConfig.xml"></property>
</bean>
<!--配置声明式事务-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通知-->
<tx:advice id="advices" transaction-manager="transactionManager">
<tx:attributes>
<!--给那些方法配置事务 一般使用 <tx:method name="*" propagation="REQUIRED"/> 全部方法都加上事务-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="pointCut" expression="execution(* com.hua.service.*.*(..))"/>
<aop:advisor advice-ref="advices" pointcut-ref="pointCut"/>
</aop:config>
</beans>
spring-service.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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
<bean class="com.hua.service.UserServiceImpl1" id="serviceImpl1">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
</beans>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserService userService = (UserService)context.getBean("serviceImpl1");
userService.getList();
}
可以看到此处在getList()方法中同时调用了添加方法和删除方法,在删除方法的sql语句故意制造错误,如果不添加事务,此处的添加方法会正常执行,这不符合事务的原则,当添加上事务之后,就会出现添加方法没效果,事务生效。