继续出差,将Spring的整体架构理顺了一遍。
解决企业应用的复杂性,使现有的技术更加容易使用,本身是一个大杂烩,整合现有的技术框架
SSH:Struct2+Spring+Hibernate
SSM:SpringMVC+Spring+MyBatis
优点:
Spring是一个开源的免费的框架(容器)
Spring是一个轻量级、非入侵式(引入不会改变代码情况)的框架
控制反转(IOC)、面向切面编程(AOP)
支持事务的处理,对框架整合的支持
弊端:发展了太久之后,违背了原来的理念,配置十分繁琐
Spring是一个轻量级的控制反转(IOC)、面向切面编程(AOP)的框架
组成
Spring Core
Spring AOP
Spring ORM
Spring DAO
Spring Web
Spring Context
Spring Web MVC
现代化的Java开发
Build构建一切(Spring Boot)
Coordinate协调一切(Spring Cloud)
Connect连接一切(Spring Cloud Data Flow)
Spring Boot
一个快速开发的脚手架
基于SpringBoot可以快速的开发单个微服务
约定大于配置
Spring Cloud
基于Spring Boot实现
大多数公司都在使用Spring Boot进行快速开发
学习SpringBoot的前提,需要完全掌握Spring及SpringMVC
依赖注入
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
2、IOC理论推导
2.1、之前开发的模式
1.UserDao接口
public interface UserDao { void getUser(); }
2.UserDaoImpl实现类
直接负责实现具体业务
3.UserService业务接口
public interface UserService { void getUser(); }
4.UserServiceImpl业务实现类
2.2、IOC的原型(控制权的转变)
在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码。如果程序代码量十分大,修改一次的成本非常昂贵。
我们使用Set接口实现,已经发生了革命性的变化。
之前,程序是主动创建对象,控制权在程序员手上
用户实际调用的是业务层Service,dao层并没有接触
(你想要什么实现类?你说完我再找给你)
UserServiceImpl userService = new UserServiceImpl();
使用set注入后,程序不再具有主动性,而是变成了被动的接收对象(控制反转)
(我这里设计好各种接口和对应的实现类,你想要哪个接口?自己取就好了)
public class UserServiceImpl implements UserService{ private UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao = userDao; } }
这种控制反转思想本质上解决了程序员管理对象的创建的问题。系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC的原型。将选择的权力交给客户。
UserServiceImpl业务实现类
package com.zhou.Service; import com.zhou.dao.UserDao; public class UserServiceImpl implements UserService{ //想要调用MySQL,只能修改Service层 //private UserDao userDao = new UserDaoImpl(); //private UserDao userDao = new UserDaoMysqlImpl(); //private UserDao userDao = new UserDaoOracleImpl(); private UserDao userDao; /** * 可以通过set方法动态实现值的注入,由用户进行set * @param userDao */ public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
实际测试类:
import com.zhou.Service.UserServiceImpl; import com.zhou.dao.UserDaoImpl; import com.zhou.dao.UserDaoMysqlImpl; import com.zhou.dao.UserDaoOracleImpl; public class MyTest { public static void main(String[] args) { //用户实际调用的是用户层,并没有接触到dao,因为客户的不同需求,需要为客户修改源代码UserServiceImpl,这个模式落后 UserServiceImpl userService = new UserServiceImpl(); //增加set注入后,将哪种实现类的选择权交给用户,自己被动接收请求,不需要对自身代码进行更改 userService.setUserDao(new UserDaoImpl()); userService.setUserDao(new UserDaoMysqlImpl()); userService.setUserDao(new UserDaoOracleImpl()); userService.getUser(); } }
2.3、本质

控制翻转,是一种设计思想,DI(依赖注入)是实现IOC的一种方法
对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
获得依赖对象的方式反转了
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体
Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)
3、HelloSpring
创建一个实体类,需要有get、set方法和toString
public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
创建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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 使用Spring来创建对象,在Spring中这些都成为Bean--> <!-- bean = 对象 –> new 类型();--> <!-- Hello hello = new Hello();--> <!-- id = 变量名--> <!-- class = new 的对象--> <!-- property 相当于给对象中的属性设置一个值--> <bean id="Hello" class="com.zhou.pojo.Hello"> <property name="str" value="Spring"/> </bean> </beans>
测试类:
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.toString()); } }
输出:Hello{str='Spring'}
到了现在,彻底不用改动内部程序了,要实现不同的操作,只需要在xml配置文件中进行修改。
IOC就是对象由Spring来创建、管理、装配
将最前边的工程做修改
测试类更改:
public class MyTest { public static void main(String[] args) { //获取ApplicationContext,拿到Spring的容器 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //类似于工厂模式 UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl"); userServiceImpl.getUser(); } }
只需要在配置文件beans.xml中注册bean信息,修改property的ref值即可,不需要对程序代码做改动
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="MysqlImpl" class="com.zhou.dao.UserDaoMysqlImpl"/> <bean id="OracleImpl" class="com.zhou.dao.UserDaoOracleImpl"/> <bean id="UserServiceImpl" class="com.zhou.Service.UserServiceImpl"> <!-- ref:引入Spring容器中已经创建好的bean对象--> <!-- value:具体的值,对应基本数据类型--> <property name="userDao" ref="MysqlImpl"/> <!-- 动态 修改 只需要修改配置,程序不用修改--> <!-- <property name="userDao" ref="OracleImpl"/>--> </bean> </beans>
4、IOC创建对象的方式
1.默认使用无参构造创建对象,如果仅仅用了简单的有参构造没有调用专门的方法时默认取消无参构造,会报错
2.如果需要使用有参构造创建对象
实体类:
package com.zhou.pojo; public class User { private String name; //public User() { // System.out.println("User无参构造"); //} public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("name="+name); } }
有参构造beans.xml
<bean id="user" class="com.zhou.pojo.User"> <!--有参构造:--> <!-- 第一种:下标赋值--> <!-- <constructor-arg index="0" value="Java"/>--> <!-- 第二种:通过类型创建,不建议使用--> <!-- <constructor-arg type="java.lang.String" value="Java"/>--> <!-- 第三种直接使用参数名,调用set,建议使用--> <constructor-arg name="name" value="Java"/> </bean>
在配置文件加载的时候,容器中管理的对象就已经实例化了
5、Spring配置
5.1、Alias别名
可以将getBean()中的名字替换
如果添加了别名,可以使用别名获取这个对象
<alias name="user" alias="AnotherUser"/>
5.2、Bean的配置(重点)
id:bean的唯一标识符,也就是相当于对象名
class:bean对象所对应的全限定名(包名.类名)
name:别名,效果和alias类型,比alias更高级,可以同时取多个别名name=“alias1,alias2,……”,可以通过逗号、分号、空格分割
<bean id="user" class="com.zhou.pojo.User" name="u0,u1 u2;u3"> <constructor-arg name="name" value="Java"/> </bean>
5.3、import导入
一般用于团队开发,可以将多个配置文件导入合并为一个
如果有多个配置文件,最后使用时只在一个配置文件,可以在此配置文件中将别的文件import
<import resource="beans.xml"/> <import resource="beans1.xml"/> <import resource="beans2.xml"/>
假设项目有多个人开发,负责不同的类开发,不同的类注册在不同的bean中,可以利用import将所有人的beans.xml合并为一个总的application.xml,且内容相同时会合并。
6、依赖注入DI
6.1、set注入(重点)
见第3节HelloSpring中的set方法注入
public void setStr(String str) { this.str = str; } <bean id="Hello" class="com.zhou.pojo.Hello"> <property name="str" value="Spring"/> </bean>
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器注入
【环境搭建】
1.复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
2.真实测试对象
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 Properties info; private String wife; }
3.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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.zhou.pojo.Student"> <!-- 第一种,普通注入:value--> <property name="name" value="zhou"/> </bean> </beans>
4.测试类
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.getName()); } }
完善注入信息:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <bean id="address" class="com.zhou.pojo.Address"/> 8 9 <bean id="student" class="com.zhou.pojo.Student"> 10 <!-- 第一种,普通注入:value--> 11 <property name="name" value="zhou"/> 12 <!-- 第二种:bean注入,ref--> 13 <property name="address" ref="address"/> 14 <!-- 数组注入 array value--> 15 <property name="books"> 16 <array> 17 <value>《红楼梦》</value> 18 <value>《西游记》</value> 19 <value>《水浒传》</value> 20 <value>《三国演义》</value> 21 </array> 22 </property> 23 <!-- List value--> 24 <property name="hobbies"> 25 <list> 26 <value>唱</value> 27 <value>跳</value> 28 <value>rap</value> 29 <value>篮球</value> 30 </list> 31 </property> 32 <!-- Map key value键值对--> 33 <property name="card"> 34 <map> 35 <entry key="身份证" value="123456123456781234"/> 36 <entry key="银行卡" value="666666666666666666"/> 37 </map> 38 </property> 39 <!-- set--> 40 <property name="games"> 41 <set> 42 <value>Minecraft</value> 43 </set> 44 </property> 45 <property name="info"> 46 <props> 47 <prop key="学号">1001</prop> 48 <prop key="性别">男</prop> 49 <prop key="年龄">20</prop> 50 </props> 51 </property> 52 <!-- null--> 53 <property name="wife"> 54 <null/> 55 </property> 56 </bean> 57 58 </beans>
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); /*Output: Student{ name='zhou', address=Address{address='null'}, books=[《红楼梦》, 《西游记》, 《水浒传》, 《三国演义》], hobbies=[唱, 跳, rap, 篮球], card={ 身份证=123456123456781234, 银行卡=666666666666666666 }, games=[Minecraft], info={ 学号=1001, 性别=男, 年龄=20 }, wife='null' } */ } }
6.2、构造器注入
见第4节无参、有参构造
有参
public User(String name) {
this.name = name;
}
<bean id="user" class="com.zhou.pojo.User"> <!--有参构造:--> <!-- 第一种:下标赋值--> <!-- <constructor-arg index="0" value="Java"/>--> <!-- 第二种:通过类型创建,不建议使用--> <!-- <constructor-arg type="java.lang.String" value="Java"/>--> <!-- 第三种直接使用参数名,调用set,建议使用--> <constructor-arg name="name" value="Java"/> </bean>
6.3、扩展方式
6.3.1、p注入
加入xmlns:p="http://www.springframework.org/schema/p"
<?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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- p命名空间注入(property),可以直接注入属性的值--> <bean id="user" class="com.zhou.pojo.User" p:name="zhou" p:age="18"/> </beans>
6.3.2、c注入
先在实体类中进行无参和有参构造
public User() { } public User(String name, int age) { this.name = name; this.age = age; }
加入xmlns:c="http://www.springframework.org/schema/c"
<?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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- c命名空间注入(constructor),通过构造器注入--> <bean id="user2" class="com.zhou.pojo.User" c:name="zhou" c:age="18"/> </beans>
注意点:不能直接使用,需要导入xml约束
6.4、bean的作用域
1.单例模式(Spring默认机制)singleton
<!-- scope singleton单例模式(默认),只创建一个实例--> <bean id="user2" class="com.zhou.pojo.User" c:name="zhou" c:age="18" scope="singleton"/>
2.原型模式prototype
每一次从容器getBean会创建新的对象
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
3.其余的request、session、application这些会在web开发中使用到
7、Bean的自动装配
-
自动装配是Spring满足bean依赖一种方式
-
Spring会在上下文中自动寻找,并自动给bean装配属性
在Spring中有三种自动装配的方式
-
在xml中显示的配置
-
在java中显示配置
-
隐式的自动装配bean(重要)
7.1、测试
环境搭建
一个人有猫、狗两个宠物
dog、cat属于people,在people类中有实例
实体类:
public class Cat { public void shout(){ System.out.println("喵!"); } } public class Dog { public void shout(){ System.out.println("汪!"); } }
package com.zhou.pojo; public class People { private Cat cat; private Dog dog; private String name; public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "People{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } }
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.zhou.pojo.Cat"/> <bean id="dog" class="com.zhou.pojo.Dog"/> <bean id="people" class="com.zhou.pojo.People"> <property name="name" value="zhou"/> <property name="dog" ref="dog"/> <property name="cat" ref="cat"/> </bean> </beans>
测试类:
public class MyTest { @Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); People people = context.getBean("people", People.class); people.getDog().shout(); people.getCat().shout(); } }
7.2、ByName自动装配
<bean id="people" class="com.zhou.pojo.People" autowire="byName"> <property name="name" value="zhou"/> <!-- 为了省去这两行代码加入autowire byName自动通过和自己对象set方法后面的值对应的bean的id查找--> <!-- <property name="dog" ref="dog"/>--> <!-- <property name="cat" ref="cat"/>--> </bean>
7.3、ByType自动装配
<bean id="people" class="com.zhou.pojo.People" autowire="byType"> <property name="name" value="zhou"/> <!-- 为了省去这两行代码加入autowire byType自动通过和自己对象属性class相同的bean查找,id可以一样或省略(需要保证类型class唯一)--> <!-- <property name="dog" ref="dog"/>--> <!-- <property name="cat" ref="cat"/>--> </bean>
小结:
ByName需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
ByType需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致,id可忽略
7.4、使用注解实现自动装配@Autowired(重要)
jdk1.5以后支持
要使用注解,需要知道
1.导入约束
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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="cat" class="com.zhou.pojo.Cat"/> <bean id="dog" class="com.zhou.pojo.Dog"/> <bean id="people" class="com.zhou.pojo.People"/> </beans>
public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; }
@Nullable 字段标记了这个注解,说明这个字段可以为Null
@Autowired默认required为true
public @interface Autowired {
boolean required() default true;
}如果显式定义了AutoWired的required属性为false,说明这个对象可以为null,否则不许为空
@Autowired(required = false)
@Autowired直接在属性上使用(可以忽略set方法,前提是这个自动装配的属性在IOC(Spring)容器中存在,且符合byType
默认是byType模式,如果多个bean类型相同,或者找不到,借助@Qualifer(value = "xxx")可以指定一个唯一的Bean的名称,走byName,否则抛出异常BeanCreationException
@Resource一般使用byName:@Resource(name = "xxx"),可以通过name和type属性进行选择性注入
当注入在IOC容器中该类型只有一个时,通过byType进行装配(不管id)
当注入容器存在多个同一类型的对象时,根据byName进行装配
@Autowired @Resource @Qualifier的区别
Autowired默认按照byType进行注入,如果发现找到多个bean,则又按照byname进行查询,如果还是多个则抛出异常。
@Autowired手动按照byName方式注入,使用@Qualifier标签
@Resource作用相当于@Autowired,只不过@Autowired按照byType自动注入,而@Resource默认按照byName注入,@Resource中有两个属性比较重要,分别是name和type,Spring将@Resource注解的name属性解析为bean名称,这种方式类似通过数据库id查询,type属性解析为bean的类型,所有如果使用name属性就用byname策略,使用type属性时候则用byType的策略,不知道时候,通过放射机制使用byName自动注入。
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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
1.bean
/** * @Component组件 等价于 <bean id="user" class="com.zhou.pojo.User"/> * 说明这个类被Spring管理了 */ @Component public class User { public String name = "zhou"; }
2.属性如何注入
/** * 相当于public String name = "zhou"; * 相当于加了setName方法后的<property name="name" value="zhou"/> */ @Value("zhou") public String name;
3.衍生的注解
@Component 有几个衍生注解,在web开发中,按照mvc三层架构分层
-
dao @Repository
-
service @Service
-
controller @Controller
这四个注解功能一样,都是代表将某个类注册到Spring中,装配Bean
4.自动装配置
-
Autowired:自动装配,通过类型byType
-
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
-
@Nullable:说明这个字段可以为null
-
@Resource:自动装配,通过名字byName
5.作用域
@Scope
@Scope("singleton")单例模式
@Scope("prototype")原型模式
6.小结
xml与注解
-
xml更加万能,适用于任何场合,维护简单方便
-
注解 不是自己的类无法使用 维护相对复杂
最佳实践:
-
xml用来管理bean
-
注解只负责完成属性的注入
-
在使用的过程中,只需要注意一个问题:必须要让注解生效,开启注解的支持
<!-- 扫描固定包,这个包下的注解生效--> <context:component-scan base-package="com.zhou"/> <context:annotation-config/>
9、使用Java的方式配置Spring
完全不使用Spring的xml配置,全程交给Java来做
JavaConfig是Spring的一个子项目,在Spring 4 之后,它成为了一个核心功能
@Configuration
实体类:
package com.zhou.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @Name User * @Description @Component 说明这个类被Spring接管了,注册到了容器中 * @Author 88534 * @Date 2021/8/25 16:35 */ @Component public class User { private String name; public String getName() { return name; } @Value("zhou") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
配置类:
package com.zhou.config; import com.zhou.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * @Name MyConfig * @Description @Configuration也会被Spring容器托管,注册到容器中,因为它本身就是一个Component * @Configuration 代表这是一个配置类,类似之前的beans.xml * @Author 88534 * @Date 2021/8/25 16:36 */ @Configuration @ComponentScan("com.zhou.pojo") @Import(MyConfig2.class) public class MyConfig { /** * 注册一个bean,就相当于之前一个bean标签,id为getUser(方法名),class为User(返回值) * @return */ @Bean public User getUser(){ //就是返回要注入到的Bean对象 return new User(); } }
测试类:
public class MyTest { public static void main(String[] args) { //完全使用配置类方式做,智能通过AnnotationConfig上下文获取容器,通过配置类的class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); User getUser = (User) context.getBean("getUser"); System.out.println(getUser.getName()); } }
这种纯Java的配置方式,在SpringBoot中随处可见
10、代理模式
中介:帮人做一些事情
为什么要学习代理模式?
因为这就是SpringAOP的底层【SpringAOP 和 SpringMVC】
代理模式的分类
-
静态代理
-
动态代理
情景模拟:

10.1、静态代理
角色分析:
-
抽象角色:一般会使用接口或者抽象类来解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,做一些附属操作
-
客户:访问代理对象的人
代码步骤:
1.接口
public interface Rent { public void rent(); }
2.真实角色 只负责简单事情
public class Landlord implements Rent{ @Override public void rent() { System.out.println("房东要出租房子!"); } }
3.代理角色
package com.zhou.demo01; /** * @Name Proxy * @Description * @Author 88534 * @Date 2021/8/26 18:15 */ public class Proxy implements Rent{ private Landlord landlord; public Proxy() { } public Proxy(Landlord landlord) { this.landlord = landlord; } @Override public void rent() { landlord.rent(); } /** * 看房 */ public void seeHouse(){ System.out.println("中介带你看房"); } /** * 收中介费 */ public void fare(){ System.out.println("收中介费"); } /** * 签租赁合同 */ public void contact(){ System.out.println("签租赁合同"); } }
4.客户端访问代理角色
package com.zhou.demo01; /** * @Name Client * @Description 客户端 * @Author 88534 * @Date 2021/8/26 18:14 */ public class Client { public static void main(String[] args) { Landlord landlord = new Landlord(); //无代理 //landlord.rent(); //代理,中介帮房东租房子,也会有一些附属操作 Proxy proxy = new Proxy(landlord); //用户不用面对房东,直接找中介租房即可 proxy.rent(); proxy.seeHouse(); proxy.fare(); proxy.contact(); } }
代理模式的好处:
-
可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
-
公共业务交给代理角色,实现了业务的分工
-
公共业务发生扩展的时候,方便集中管理
缺点:
-
一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率降低
10.2、加深理解
再举一个例子:
1 public interface UserService { 2 public void add(); 3 public void delete(); 4 public void update(); 5 public void query(); 6 } 7 public class UserServiceImpl implements UserService{ 8 public void add() { 9 System.out.println("增加了一个用户"); 10 } 11 12 public void delete() { 13 System.out.println("删除了一个用户"); 14 } 15 16 public void update() { 17 System.out.println("修改了一个用户"); 18 } 19 20 public void query() { 21 System.out.println("查询了一个用户"); 22 } 23 } 24 public class UserServiceProxy implements UserService{ 25 private UserServiceImpl userService; 26 27 public void setUserService(UserServiceImpl userService) { 28 this.userService = userService; 29 } 30 31 public void add() { 32 log("add"); 33 userService.add(); 34 } 35 36 public void delete() { 37 log("delete"); 38 userService.delete(); 39 } 40 41 public void update() { 42 log("update"); 43 userService.update(); 44 } 45 46 public void query() { 47 log("query"); 48 userService.query(); 49 } 50 51 public void log(String msg){ 52 System.out.println("使用了" + msg + "方法"); 53 } 54 } 55 public class Client { 56 public static void main(String[] args) { 57 UserServiceImpl userService = new UserServiceImpl(); 58 UserServiceProxy proxy = new UserServiceProxy(); 59 proxy.setUserService(userService); 60 proxy.add(); 61 proxy.delete(); 62 proxy.update(); 63 proxy.query(); 64 } 65 }
保障原有UserServiceImpl代码不变的基础上,通过代理类实现扩展业务
一般业务办理流程:dao——>service——>controller——>前端(纵向开发)
为了防止添加代码有问题造成系统崩溃无法排查,在相应模块通过代理类增加扩展代码,实现横向开发(切面)
10.3、动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是直接写好的
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
-
基于接口:JDK动态代理
-
基于类:cglib
-
Java字节码实现:Javassist
需要了解:class Proxy:代理,interface InvocationHandler:调用处理程序
动态代理的好处:
-
具有静态代理的所有好处
-
复用成本低,一个动态代理类可以代理多个类,只要是实现了同一个接口即可。(一个接口可以有多个实现类)
示例代码:
保留原工程的rent和Landlord,增加代理类和代理处理程序
package com.zhou.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } /** * 通过反射生成得到代理类,创建一个新的代理实例 * @return */ public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); } /** * 处理代理实例,并返回结果 * @param proxy * @param method * @param args * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现接口的方法,并返回 Object result = method.invoke(rent, args); return result; } }
客户端:
package com.zhou.demo03; /** * @Name Client * @Description * @Author 88534 * @Date 2021/8/27 20:42 */ public class Client { public static void main(String[] args) { //真实角色 Landlord landlord = new Landlord(); //代理角色 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来设置要代理的接口对象 pih.setRent(landlord); //动态生成代理类 Rent proxy = (Rent) pih.getProxy(); proxy.rent(); } }
11、AOP
11.1、什么是AOP
AOP(Aspect Oriented Programming)面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是Spring框架的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合关系降低,提高程序的可重用性,同时提高了开发的效率。
11.2、AOP在Spring中的作用
提供声明式服务,允许用户自定义切面。
-
横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关,但是需要关注的部分。如日志、安全、缓存、事务等等。
-
切面(Aspect):被模块化的特殊对象,是一个类
-
通知(Advice):切面必须要完成的工作,是类中的一个方法
-
目标(Target):被通知对象
-
代理(Proxy):向目标对象应用通知之后创建的对象
-
切入点(PointCut):切面通知,执行的"地点"的定义
-
连接点(JointPoint):与切入点匹配的执行点
Spring AOP 中,通过Advice定义横切逻辑,Spring中支持5中类型的Advice
| 通知类型 | 连接点 | 实现接口 |
|---|---|---|
| 前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
| 后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
| 环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
| 异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
| 引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductuinInterceptor |
即AOP在不改变原有代码的情况下,去增加新的功能
11.3、使用Spring实现AOP
【重点】使用AOP植入,需要导入一个依赖包
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>
【环境搭建】和之前的UserService类似
public interface UserService { public void add(); public void delete(); public void update(); public void query(); } public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void query() { System.out.println("查询了一个用户"); } }
11.3.1、实现Spring的API接口(更强大)
增加通知方法 其他通知方法类似
package com.zhou.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class BeforeLog implements MethodBeforeAdvice { /** * 前置通知 * @param method 要执行的目标对象的方法 * @param objects 参数 * @param target * @throws Throwable */ public void before(Method method, Object[] objects, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
package com.zhou.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue); } }
增加Spring配置文件applicationContext.xml
引入aop需要增加依赖
<?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"> <!-- 注册bean--> <bean id="userService" class="com.zhou.service.UserServiceImpl"/> <bean id="beforeLog" class="com.zhou.log.BeforeLog"/> <bean id="afterLog" class="com.zhou.log.AfterLog"/> <!-- 方法一:使用原生Spring API接口--> <!-- 配置AOP:需要导入aop的约束--> <aop:config> <!-- 切入点 express:表达式 execution(返回类型(*表示全部) 包名.类名 方法名(参数,..表示全部))--> <aop:pointcut id="pointCut" expression="execution(* com.zhou.service.UserServiceImpl.*(..))"/> <!-- 执行环绕增强--> <aop:advisor advice-ref="beforeLog" pointcut-ref="pointCut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointCut"/> </aop:config> </beans>
调试类:
import com.zhou.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理代理的是接口 UserService userService = context.getBean("userService", UserService.class); userService.add(); } }
调试结果:
com.zhou.service.UserServiceImpl的add被执行了 增加了一个用户 执行了add方法,返回结果为null
11.3.2、自定义实现AOP
自定义需要执行的操作
public class DiyPointCut { public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }
只需要修改Spring配置文件
<!-- 方式二、自定义类--> <bean id="diy" class="com.zhou.diy.DiyPointCut"/> <aop:config> <!-- 自定义切面:ref要引入的类--> <aop:aspect ref="diy"> <!-- 切入点--> <aop:pointcut id="point" expression="execution(* com.zhou.service.UserServiceImpl.*(..))"/> <!-- 通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
调试结果:
方法执行前 增加了一个用户 方法执行后
11.3.3、使用注解实现
execution(返回类型(*表示全部) 包名.类名 方法名(参数,..表示全部)
@Aspect public class AnnotationPointCut { @Before("execution(* com.zhou.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前"); } @After("execution(* com.zhou.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后"); } }
修改Spring配置文件
<!-- 方式三、使用注解--> <bean id="annotationPointCut" class="com.zhou.diy.AnnotationPointCut"/> <!-- 开启注解支持,JDK默认为proxy-target-class="false",cglib为"true”--> <aop:aspectj-autoproxy proxy-target-class="false"/>
调试结果如上
再增加一个环绕类型在后
包裹在外边
@Around("execution(* com.zhou.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//获得签名
//Signature signature = jp.getSignature();
//System.out.println("signature:" + signature);
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
}
执行结果:
环绕前 方法执行前 增加了一个用户 方法执行后 环绕后
12、整合MyBatis
步骤:
1.导入相关jar包
-
junit
-
mybatis
-
mysql数据库
-
aop植入
-
mybatis-spring【new】
1 <dependency> 2 <groupId>junit</groupId> 3 <artifactId>junit</artifactId> 4 <version>RELEASE</version> 5 <scope>test</scope> 6 </dependency> 7 <dependency> 8 <groupId>mysql</groupId> 9 <artifactId>mysql-connector-java</artifactId> 10 <version>5.1.47</version> 11 </dependency> 12 <dependency> 13 <groupId>org.mybatis</groupId> 14 <artifactId>mybatis</artifactId> 15 <version>3.5.2</version> 16 </dependency> 17 <dependency> 18 <groupId>org.springframework</groupId> 19 <artifactId>spring-webmvc</artifactId> 20 <version>5.2.8.RELEASE</version> 21 </dependency> 22 <!-- Spring操作数据库,需要一个spring-jdbc--> 23 <dependency> 24 <groupId>org.springframework</groupId> 25 <artifactId>spring-jdbc</artifactId> 26 <version>5.2.8.RELEASE</version> 27 </dependency> 28 <dependency> 29 <groupId>org.aspectj</groupId> 30 <artifactId>aspectjweaver</artifactId> 31 <version>1.9.4</version> 32 </dependency> 33 <dependency> 34 <groupId>org.mybatis</groupId> 35 <artifactId>mybatis-spring</artifactId> 36 <version>2.0.6</version> 37 </dependency> 38 <dependency> 39 <groupId>org.projectlombok</groupId> 40 <artifactId>lombok</artifactId> 41 <version>1.18.16</version> 42 </dependency> 43 </dependencies> 44 45 <build> 46 <resources> 47 <resource> 48 <directory>src/main/java</directory> 49 <includes> 50 <include>**/*.xml</include> 51 </includes> 52 <filtering>true</filtering> 53 </resource> 54 </resources> 55 </build>
2.编写配置文件
3.测试
12.1、回忆Mybatis
1.编写实体类
@Data public class User { private int id; private String name; private String pwd; }
2.编写核心配置文件
mybatis-config.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> <properties resource="db.properties"/> <typeAliases> <package name="com.zhou.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.zhou.mapper.UserMapper"/> </mappers> </configuration>
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
username=root
password=root
3.编写接口
public interface UserMapper { List<User> selectUser(); }
4.编写Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zhou.mapper.UserMapper"> <select id="selectUser" resultType="user"> select * from mybatis.user; </select> </mapper>
5.测试
import com.zhou.mapper.UserMapper; import com.zhou.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * @Name MyTest * @Description * @Author 88534 * @Date 2021/8/28 11:10 */ public class MyTest { @Test public void test() throws IOException { String resources = "mybatis-config.xml"; InputStream in = Resources.getResourceAsStream(resources); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sessionFactory.openSession(true); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user : users) { System.out.println(user); } } }
测试结果:
User(id=1, name=zhou, pwd=123456) User(id=2, name=rong, pwd=234567) User(id=3, name=ming, pwd=345678) User(id=4, name=ling, pwd=456789)
12.2、MyBatis-Spring
12.2.1、使用sqlSessionTemplate创建(核心)
1.编写数据源配置
<!-- DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid--> <!-- 使用Spring提供的JDBC--> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
2.sqlSessionFactory
<!-- sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource" /> <!-- 绑定Mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/zhou/mapper/*.xml"/> </bean>
3.sqlSessionTemplate
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
4.需要给接口加实现类UserMapperImpl,将SqlSessionTemplate私有化,且处理mapper信息
public class UserMapperImpl implements UserMapper{ /** * 原本所有操作都使用sqlSession执行,现在都使用SqlSessionTemplate */ private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } public List<User> selectUser() { UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class); return mapper.selectUser(); } }
5.将自己写的实现类UserMapperImpl,注入到Spring中
<bean id="userMapper" class="com.zhou.mapper.UserMapperImpl"> <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> </bean>
6.测试
public class MyTest { @Test public void test() throws IOException { ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } } }
测试结果同12.1
User(id=1, name=zhou, pwd=123456) User(id=2, name=rong, pwd=234567) User(id=3, name=ming, pwd=345678) User(id=4, name=ling, pwd=456789)
此时mybatis-config.xml已经简化为只需负责别名和设置即可,其余均交给Spring实现
<?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> <!-- 别名--> <typeAliases> <package name="com.zhou.pojo"/> </typeAliases> <!-- 设置--> <!-- <settings>--> <!-- <setting name="" value=""/>--> <!-- </settings>--> </configuration>
为了方便后期管理,也可以整合为一个总文件和几个分系统文件,方便管理,总文件将别的模块import后就可使用。
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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="spring-dao.xml"/> <bean id="userMapper" class="com.zhou.mapper.UserMapperImpl"> <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> </bean> </beans>
12.2.2、使用SqlSessionDaoSupport创建
直接实现类继承SqlSessionDaoSupport,省去私有化步骤
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{ public List<User> selectUser() { SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); //合并:return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
在Spring配置xml文件applicationContext.xml中引入
<bean id="userMapper2" class="com.zhou.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
测试
public class MyTest { @Test public void test() throws IOException { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper2", UserMapper.class); List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } } }
与上述结果相同
13、声明式事务
13.1、回顾事务
-
要么都成功,要么都失败
-
事务在项目开发中,十分重要,涉及到数据的一致性问题,不能马虎
-
确保完整性和一致性
事务ACID原则:
-
原子性Atomicity
-
一致性Consistency
-
隔离性Isolation
-
多个业务可能操作同一个资源,防止数据损坏
-
-
持久性Durability
-
事务一旦提交,无论系统发生什么问题,结果都不会被影响,被持久化写到存储器中
-
13.2、Spring中的事务管理
声明式事务:AOP【重点】
编程式事务:需要在代码时,进行事务的管理(使用的较少)
为什么需要事务?
如果不配置事务,可能存在数据提交不一致的情况
如果不在Spring中配置声明式事务,就需要在代码中手动配置事务
事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎。
示例代码:
1.配置接口
public interface UserMapper { List<User> selectUser(); int addUser(User user); int deleteUser(@Param("id") int id); }
2.配置UserMapper.xml
此时delete的sql语句有语法错误(deletes)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zhou.Mapper.UserMapper"> <select id="selectUser" resultType="user"> select * from mybatis.user; </select> <insert id="addUser" parameterType="user"> insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd}); </insert> <delete id="deleteUser" parameterType="int"> deletes from mybatis.user where id = #{id}; </delete> </mapper>
3.实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ public List<User> selectUser() { User user = new User(5, "wang", "123123"); UserMapper mapper = getSqlSession().getMapper(UserMapper.class); //新增操作 mapper.addUser(user); mapper.deleteUser(5); return mapper.selectUser(); } public int addUser(User user) { return getSqlSession().getMapper(UserMapper.class).addUser(user); } public int deleteUser(int id) { return getSqlSession().getMapper(UserMapper.class).deleteUser(id); } }
4.配置文件
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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="spring-dao.xml"/> <bean id="userMapper" class="com.zhou.Mapper.UserMapperImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> </beans>
mybatis-config.xml
与前项目一致
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:tx="http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd" 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"> <!-- DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid--> <!-- 使用Spring提供的JDBC--> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource" /> <!-- 绑定Mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/zhou/Mapper/*.xml"/> </bean> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <bean id="userMapper" class="com.zhou.Mapper.UserMapperImpl"> <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/> </bean> <!-- 配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- <constructor-arg ref="datasource"/>--> <property name="dataSource" ref="datasource"/> </bean> <!-- 结合AOP实现事务的注入--> <!-- 配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给那些方法配置事务--> <!-- 配置事务的传播特性 new propagation--> <tx:attibute> <tx:method name = "add" propagation="REQUIRED"/> <tx:method name = "delete" propagation="REQUIRED"/> <tx:method name = "update" propagation="REQUIRED"/> <tx:method name = "query" read-only="true"/> <tx:method name = "*" propagation="REQUIRED"/> </tx:attibute> </tx:advice> <!-- 配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.zhou.Mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>
5.测试类
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } }
如果不配置事务,将会执行add插入数据,但错误的delete语句不会进行,导致数据库多了一条不应该的数据,破坏了ACID原则
浙公网安备 33010602011771号