Spring框架是由软件开发的复杂性而创建的,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建。---百度百科
Spring框架由以下的模块构成,一一进行学习。
1、IOC容器
1.1 之前Java代码的学习中,创建对象常常通过new构造器的方式实现。随着业务不断的复杂,代码量不断增加,有时候new的方式不太方便。这时候,可以使用Spring中IOC容器进行实例化对象。
图解:你所要创建的实体类对象,通通放进Spring容器中,它为你进行保管;在读取一些配置,你就可以从Spring容器中使用这些对象,而不用通过手动new的方式创建。
1.2.1 基于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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
1.2.2 如何通过这种方法进行对象的创建?
1.创建实体类Hello。
public class Hello {
private String str;
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
注意:务必对属性添加set方法,这是依赖注入的关键!
2.通过xml文件配置IOC容器(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来创建对象,对象都称作Bean
以前使用new的方式来创建对象
对象类型 对象名 = new对象类型()
Hello hello = new Hello()
使用spring后:
id:对象名
class:对象类型的全路径
property:给对象的属性设置值
-->
<bean id="hello" class="com.leo.pojo.Hello">
<property name="str" value="Spring"></property>
</bean>
</beans>
这样,一个id为"hello"的对象就已经存在于该IOC容器中,随时准备调用。
如果想在该IOC容器中同时生成多个待用对象,重复此代码即可(id不可重复):
<bean id="id名" class="com.leo.pojo.Hello"> <property name="str" value="Spring"></property> </bean>
1.2.3 如何获取(调用)该容器中的对象?
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
使用此代码能够获取该IOC容器,括号内就填上你所配置好的xml文件名。一旦获取了IOC容器,所有在xml文件中存在的bean(对象)都会生成,等待调用。
Hello hello= context.getBean("hello");
通过此代码,你就获得了你在xml文件中生成的id为"hello"的对象,并可以进行使用,与new方法创建的对象一致。
完整代码:
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象(bean)都在Spring中管理,要使用时从里面取出来即可
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
结果:
1.3 生成bean并给属性赋值的方式
1.3.1:通过有参构造器注入。
实体类:
public class User {
private String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
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);
}
}
此类中存在有参(name)构造器。对于有参构造器形式的注入,xml中有3方式。
xml:
<!--有参构造器的3种实现方式-->
<!--方式1:通过下标赋值-->
<!--<bean id="user" class="com.leo.pojo.User">-->
<!--<constructor-arg index="0" value="leo"/>-->
<!--</bean>-->
<!--方式2:通过类型赋值 不推荐-->
<!--<bean id="user" class="com.leo.pojo.User">-->
<!--<constructor-arg type="java.lang.String" value="leooo"/>-->
<!--</bean>-->
<!--方式3:通过参数名称赋值 推荐使用-->
<bean id="user" class="com.leo.pojo.User">
<constructor-arg name="name" value="乐子"/>
</bean>
通过<constructor/>标签来实现构造器注入值的方式如上。
方式1:通过下标(类似于数组下标)。若存在构造器public Xxx(String name,int age,float salary...),则index=0则给name属性赋值,index=1给age属性赋值...
方式2:通过类型赋值(不推荐)。public Xxx(String name,int age,float salary...),通过type="java.lang.String"给name属性赋值,type="int"给age属性赋值...但是,如果构造器中存在同一种类型的多个参数,那么此方法就会失效。
方式3:通过参数名称赋值(推荐使用)。public Xxx(String name,int age,float salary...)中,<constructor-arg name="name" value="乐子"/>,name=你所要赋值的属性,value则为你所想赋的值。使用此方法比较清晰,很容易将属性名与值对应。<constructor-arg name="age" value="13"/>则给age属性赋了值。
1.3.2:通过set注入。
通过set注入的前提是为该属性提供set方法。通过set方式注入,往往能注入一些复杂类型的属性,或者是注入多元素的属性(集合、数组...)时更加方便。
实体类(Student)
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;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
}
该类中存在着基本类型的属性name,非基本类型address,以及集合类型等属性。接下来探究如何通过set注入的方式给他们赋值。
public class Address {
private String address;
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
1.3.2.1:注入普通类型的值:
普通类型的值只需要在bean标签中用name-value注入。
<!--注入普通值 value-->
<property name="name" value="关公"/>
1.3.2.2:注入非普通类型的值(注入对象):
注入对象通过name-ref注入,前提是已经存在了一个待注入的bean。
<!--创建bean-->
<bean id="address" class="com.leo.pojo.Address">
<property name="address" value="海垦路"/>
</bean>
<!--注入bean ref-->
<property name="address" ref="address"/>
1.3.2.3:注入数组:
property中提供了<array/>这一标签以供数组注入。
<!--注入数组-->
<property name="books">
<array>
<value>红楼梦</value>
<value>三国演义</value>
<value>水浒传</value>
<value>西游记</value>
</array>
</property>
1.3.2.4:注入List集合
<list/>标签注入即可。
<!--注入list-->
<property name="hobbies">
<list>
<value>吹牛逼</value>
<value>健身</value>
<value>打游戏</value>
</list>
</property>
1.3.2.5:注入Map
<map/>标签,采用的是entry key-value方式注入了键值对
<!--注入map-->
<property name="card">
<map>
<entry key="学号" value="20010100"/>
<entry key="身份证" value="19010100103"/>
</map>
</property>
1.3.2.6:注入Set
<set/>标签
<!--注入set-->
<property name="games">
<set>
<value>LOL</value>
<value>2K</value>
<value>FIFA</value>
</set>
</property>
1.3.2.7:注入null
<!--注入null-->
<property name="wife">
<null/>
</property>
注意:注入null,必须是通过<null/>标签,若是value=""注入的则是空字段(非null)
1.3.2.8:注入propertie:
<!--注入properties-->
<property name="info">
<props>
<prop key="性别">男性</prop>
<prop key="年龄">22</prop>
</props>
</property>
注意与注入map的区别。
1.3.3:利用p和c命名空间注入属性。
p(properties)和c(constructor)命名空间注入属性来简化上述两种方式注入属性。如果想使用,首先在xml文件的顶部添加如下两行
xmlns:p="http://www.springframework.org/schema/p" 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: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命名空间,通过set方法注入-->
<bean id="user" class="com.leo.pojo.User" p:name="张亮" p:age="13" scope="prototype"/>
<!--c命名空间,通过构造器注入-->
<bean id="user2" class="com.leo.pojo.User" c:age="22" c:name="王大厨"/>
当引入p和c命名空间之后,bean字段中会出现p:和c: ,填入相关属性即可。
1.4. bean的作用域。
在上面的p命名空间注入属性后,可以看见后面有scope这么一个属性。scope,就是该bean的作用域,存在着4个值,分别是:singleton(默认),prototype,request,session。其中后两个是针对于web应用中的,这里就介绍前2个singleton和prototype。
singleton:如果把一个bean的作用域设置成这个,那么每次从IOC容器中获取的这个bean都是同一个对象。换而言之spring只会产生一个该对象。
prototype:每次从IOC容器中获取这个bean,都会返回不同的对象。就是说spring会在每次获取时都产生新的实例。
1.5 autowired自动注入
定义3个实体类如下:
public class Cat {
public void shout(){
System.out.println("miao!");
}
}
public class Dog {
public void shout(){
System.out.println("wang!");
}
}
public class People {
private Dog dog;
private Cat cat;
private String name;
@Override
public String toString() {
return "People{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
people类中存在着Cat和Dog类型的属性。我们给属性全部设置set方法,以便后续注入值。
根据之前所学的知识,若想给dog和cat属性都注入值,需要通过property-ref的方式:
<bean id="cat" class="com.leo.pojo.Cat"/>
<bean id="dog" class="com.leo.pojo.Dog"/>
<bean id="people" class="com.leo.pojo.People">
<property name="name" value="中国"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat"/>
</bean>
然而,在有了自动注入之后,对于dog和cat属性,我们可以省略手动的注入值,由spring帮我们自动的注入。spring提供了2中自动注入的方式:byType和byName。
byType:根据类型。spring会根据该属性的类型,自动寻找IOC容器中存在且符合该类型的bean(相同class),将其自动注入:
<bean id="cat" class="com.leo.pojo.Cat"/>
<bean id="dog" class="com.leo.pojo.Dog"/>
<bean id="people" class="com.leo.pojo.People" autowire="byType">
<property name="name" value="中国"/>
</bean>
byName:根据名称。spring自动的在IOC容器中寻找和实体类中属性名一致的bean id的bean来自动注入:
<bean id="cat" class="com.leo.pojo.Cat"/>
<bean id="dog" class="com.leo.pojo.Dog"/>
<bean id="cat2" class="com.leo.pojo.Cat"/>
<bean id="dog2" class="com.leo.pojo.Dog"/>
<bean id="people" class="com.leo.pojo.People" autowire="byName">
<property name="name" value="hiLeo"/>
</bean>
由于People类中属性名为cat和dog,所以注入了前两行的bean。
1.6 使用注解开发。
如果IOC容器中存在大量的bean时,通过在xml文件中添加,代码量极大。使用注解开发可以极大地减少代码量,并让代码看起来更加简单。
之前的开发中,我们想在IOC容器中注册一个bean,都是通过在xml文件中添加<bean/>标签来实现。通过注解开发又如何实现?
1.6.1 使用注解开发的准备工作
<context:annotation-config/>
<context:component-scan base-package=""/>
将这两行的代码放到你的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: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/>
<!--扫描包下注解-->
<context:component-scan base-package="com.leo"/>
</beans>
1.6.2 使用注解开发
使用了注解开发后我们以后创建对象就不需要在xml文件中添加,只需要通过注解即可实现。
@Component
@Scope("singleton")
public class User {
@Value("达伦1")//优先
public void setName(String name) {
this.name = name;
}
@Value("达伦")//相当于注入了property的value
public String name;
}
@Component:相当于<bean id=user class="....User">
@Scope:设置该bean的作用域,默认为singleton
@value:放在属性或者set上,给属性注入值。如果两者头上都有此注解,则属性值是被注入set方法上的。
@Controller、@Repository、@Service,作用都和@Component一致,新建对象。只是使用的层不同,分别用在controller、dao、service、pojo层。
1.6.3 使用注解自动注入
使用注解也可以完成自动注入的功能,我们将上面自动注入的例子修改:
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: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/>
<context:component-scan base-package="com.leo.pojo"/>
</beans>
@Repository//自动创建bean,id为cat
public class Cat {
public void shout(){
System.out.println("miao!");
}
}
@Repository//自动创建id为dog的bean
public class Dog {
public void shout(){
System.out.println("wang!");
}
}
@Repository
public class People {
@Autowired(required = false)//直接在属性上使用。也可以在set方法上.如果这样显示标注了false,则可以为空
@Qualifier(value = "dog")//指定的寻找dog并装配
private Dog dog;
@Autowired//使用此可以省略set方法,前提是该bean必须在ioc中存在且名字一致
@Qualifier(value = "cat")
//@Resource(name="")注解也可以
private Cat cat;
private String name;
@Override
public String toString() {
return "People{" +
"dog=" + dog +
", cat=" + cat +
", name='" + name + '\'' +
'}';
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Autowired:进行byType自动装备
@Qualifier:用于@Autowired下面,一起使用,用于在存在多个同类型的bean时指定id注入
@Resource(name=""):集成上面2个注解的功能,一个顶俩。
1.7 使用配置类代替xml文件开发
之前我们都是使用applicationContext.xml文件进行开发,现在我们可以改成使用配置类来代替配置文件。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//使用配置文件创建IOC容器
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);//使用配置类创建IOC容器
创建IOC容器的方式改变了。
1.7.1 创建配置类的注解
@Configuration:类上标有此注解说明为配置类
@Bean:在方法上添加该注解,作用等同于xml文件中的<bean/>标签
@Bean//生成User实例
public User getUser(){//id
return new User();//class
}
这样就相当于在IOC容器中创建了一个user bean。
1.7.2 使用IOC中的bean
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);//用config类创建IOC容器
User user = context.getBean("getUser", User.class);//方法名
System.out.println(user);
}
2.AOP(面向切面编程)
2.1 在业务中,往往代码已经写死。这时候如果想在其中添加某项功能,直接修改源代码的方式是不合适的,这样会破坏原来的代码。这时候使用aop可以在不修改源代码下添加功能,降低了代码的耦合性。aop的本质是动态代理。
在实现AOP前必须加入以下依赖aspectj
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
2.2 实现切面编程
2.2.1 方式1:使用spring接口来实现(推荐)
spring提供了几个常用的接口实现aop:
MethodInterceptor:环绕advice
MethodBeforeAdvice:before advice
AfterReturningAdvice:after advice
编写接口:
public interface UserService {
void add();
void delete();
void query();
void update();
}
实现类:
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
}
编写通知(待插入的功能):
MethodBeforeAdvice:简单的前置通知,在待增强的方法前调用
AfterReturningAdvice:后置通知,在待增强方法后调用,存在返回值。
public class Log implements MethodBeforeAdvice {
//method:需要被代理的方法
//objects:方法中的参数
//o:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(method.getClass().getName()+"类的前"+method.getName()+"被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//o:返回值
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(method.getClass().getName()+"后执行了"+method.getName()+"方法,结果为:"+o);
}
}
编写配置类:
<!--方式1:采用spring接口-->
<!--注册bean-->
<bean id="userService" class="com.leo.service.UserServiceImpl"/>
<bean id="log" class="com.leo.log.Log"/>
<bean id="afterLog" class="com.leo.log.AfterLog"/>
<!--配置aop-->
<aop:config>
<!--切入点pointcut,expression表达式,execution(...):需要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* com.leo.service.UserServiceImpl.*(..))"/>
<!--执行环绕增强 advice-ref:等待加入的类bean;pointcut:需要切入的切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试结果:
2.2.2 使用自定义增强类来实现
接口,实现类代码不变,自定义一个增强类:
public class DiyPointcut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
xml文件:
<!--方式2:自定义类-->
<!--注册bean-->
<bean id="userService" class="com.leo.service.UserServiceImpl"/>
<bean id="diy" class="com.leo.diy.DiyPointcut"/>
<aop:config>
<!--自定义切面 ref:要引用的类-->
<aop:aspect ref="diy">
<!--定义切入点-->
<aop:pointcut id="point" expression="execution(* com.leo.service.UserServiceImpl.*(..))"></aop:pointcut>
<!--定义通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
结果:
2.2.3 使用注解实现
使用注解时必须在配置文件中开启注解扫描
<aop:aspectj-autoproxy/>
增强类:
@Aspect//标记为一个切面
public class AnnoPiontCut {
@Before("execution(* com.leo.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("增强之前");
}
@After("execution(* com.leo.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("增强之后");
}
@Around("execution(* com.leo.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Signature signature = jp.getSignature();//获取签名
System.out.println(signature);
jp.proceed();//执行方法
System.out.println("环绕后");
}
}
@Before:待增强方法前执行
@After:待增强方法后执行
@Around:环绕待增强方法执行
配置文件:
<!--方式3:使用注解方式-->
<bean id="annoPiontCut" class="com.leo.diy.AnnoPiontCut"/>
<bean id="userService" class="com.leo.service.UserServiceImpl"/>
<!--开启注解扫描-->
<aop:aspectj-autoproxy/>
结果:
3.spring-mybatis
spring中整合mybatis,可以完成mybatis的操作。
3.1 导入相关依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3.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>
<typeAliases>
<package name="com.leo.pojo"/>
</typeAliases>
</configuration>
这个配置文件可以整合到其他的配置文件中,留下是为了说明使用了mybatis。
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--Spring注入Datasource数据源替换Mybatis的
这里使用Spring的JDBC:org.springframework.jdbc.datasource.DriverManagerDataSource
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306"/>
<property name="username" value="root"/>
<property name="password" value="gzx123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/leo/Mapper/*.xml"/>
</bean>
<!--SqlSessionTemplate:相当于sqlSession 必须使用有参构造注入sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
此配置文件可以用作整合mybatis-spring的模板,使用时只需要修改成对应的值即可。
3.3 spring中操作数据库
3.3.1 方式一:手动创建SqlSessionTemplate
3.3.1.1 写接口
public interface UserMapper {
List<User> selectUser();
}
3.3.1.2 写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.leo.Mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from mybatis.user;
</select>
</mapper>
3.3.1.3 编写接口对应的实现类(与mybatis不同)
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
//set注入
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
在spring中操作数据库,必须引入SqlSessionTemplate类,此类相当于mybatis中的sqlSession。
实现的方法中,也是通过SqlSessionTemplate类的对象操作。
3.3.1.4 新建一个applicationContext.xml, 在IOC容器中注入实现类的对象
<?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">
<import resource="spring-dao.xml"/>//引入spring-mybatis模板配置
<!--注入实现类-->
<bean id="userMapper" class="com.leo.Mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
这样就可以通过spring操作数据库了。
测试:
@Test
public void myTest1() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
结果:
3.3.2 通过继承自动获取SqlSessionTemplate
其他的地方不变,接口实现类改为如下:
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();
}
}
继承了SqlSessionDaoSupport类,可以直接通过getSession方法获取sqlSession来操作数据库。
测试结果相同。
3.4 spring中操作事务。
3.4.1 在mybatis-spring的模板的xml文件中添加事务声明
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
<!--结合aop实现事务的植入-->
<!--配置事务通知-->
<tx:advice id="txAdvisor" transaction-manager="transactionManager">
<!--需要配置事务的方法-->
<!--propagation:事物的传播性-->
<tx:attributes>
<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:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.leo.Mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvisor" pointcut-ref="txPointCut"/>
</aop:config>
将这段代码添加到上面的模板中,作为支持事务的最终模板
3.4.2 写接口
public interface UserMapper {
List<User> selectUser();
int addUser(User user);
int deleteUser(int id);
}
3.4.3 写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.leo.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,手动制造错误
</delete>
</mapper>
3.4.3 写实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
private User user = new User(12,"王打雷","wwdl");
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(11);//这里的deleteUser方法有错误
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);
}
}
这里手动的制造了一个错误,按事务的一致性来说,应该一个操作都不执行。
3.4.4 测试后结果:
报错,sql语法错误。
此时数据库:
确实没有增加新用户。
将语法修改正确后,在执行,按理来说应该增加id为12的用户,删除id为11的。
结果: