spring组成七大模块:
1.为了使用spring,需要导入依赖。下面这些,只需要导入Spring-Web-MVC这个包,它就会自动导入其他的核心、上下文等相关依赖包。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
下面就可以使用spring了。
一.第一个spring项目
1.spring的简单使用
建立一个实体类:有一个属性str
package com.han.pojo;
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 + '\'' +
'}';
}
}
spring中所有的类都是要注入容器中的,由spring托管、创建、装配bean,不再由开发人员去new一个对象,这就是IOC(控制反转)。这里先使用配置文件的方式。在resource下面新建一个配置文件beans.xml(即ApplicationContext .xml):
id是每个bean的唯一标识符,也是创建的对象hello,class是对象类型Hello。
property是bean里面的属性,name是属性名str,value是给这个属性赋初始值。
基本类型使用value。
<?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="hello" class="com.han.pojo.Hello">
<property name="str" value="Spring"></property>
</bean>
</beans>
对于基本类型属性,通过value直接赋值。如果此时有一个实体类Person,而在Hello类里面,有一个私有属性:private Person person。对person属性,需要通过ref:
<bean id="person" class="com.han.pojo.Person"/>
<bean id="hello" class="com.han.pojo.Hello">
<property name="str" value="Spring"></property>
<property name="person" ref="person"></property>
</bean>
建立测试类
使用配置文件,这里获取上下文的方式就固定为(获取配置文件):
ApplicationContext context=new ClassPathXmlApplicationContext(“beans.xml”);
import com.han.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
Hello hello=(Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
注意:上面在创建hello实例时,做了一次类型强制转为Hello类型。如果不想强制转换,可以使用反射:
Hello hello= context.getBean("hello",Hello.class);
注:
在spring中,对象是由spring创建的,对象的属性是由spring赋值的,hello对象的属性str注入spring是通过str的set()方法,所以在Hello实体类里面属性必须要有其对应set()方法。
在spring中,对象hello的创建默认是通过实体类Hello的无参构造创建的,在加载配置文件之前,对象就通过无参构造创建好了。如果实体类中没有无参构造,则对象无法被创建。
二.spring对有参构造的属性赋值
假设此时实体类中只有有参构造,它覆盖了默认存在的无参构造。spring提供了几种使用有参构造给属性赋值的方式(IOC创建对象的三种方式)。再新建一个实体类,它只有一个有参构造:
package com.han.pojo;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
此时bean注入和上面一样,只是属性赋值发生了改变。
第一种,通过下表index赋值:
<bean id="user" class="com.han.pojo.User">
<constructor-arg index="0" value="张三"/>
</bean>
第二种,通过类型赋值,但是如果实体类有多个String类型的属性,就不能使用这种方法了,不推荐使用这种方式:
<bean id="user" class="com.han.pojo.User">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
第三种,通过属性名赋值
<bean id="user" class="com.han.pojo.User">
<constructor-arg name="name" value="张三"/>
</bean>
测试
public class SpringTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user=context.getBean("user", User.class);
System.out.println(user.toString());
}
}
三.spring中bean的作用域
下面再测试这样的一段代码(bean作用域):
同一个bean,取出两个对象,发现它们是相等的。
public class SpringTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user1=context.getBean("user", User.class);
User user2=context.getBean("user",User.class);
System.out.println(user1 == user2);
}
}
这是因为spring默认bean的模式为单例模式(scope=“singleton”),这两个对象是在内存中是同一个实例。
<bean id="user" class="com.han.pojo.User" scope="singleton">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
如果改为原型模式(scope=“prototype”)
<bean id="user" class="com.han.pojo.User" scope="prototype">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
此时测试user1==user2的结果就是false了,每一个创建的对象都是独立的。
下面介绍spring的简单配置
四.spring中bean设置别名的方式
别名Alias(只能设置一个别名)
<bean id="user" class="com.han.pojo.User" scope="singleton">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
对于这个bean,user。可以通过alisa起一个user1
<bean id="user" class="com.han.pojo.User" scope="singleton">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
<alias name="user" alias="user1"/>
或者直接通过name设置一个或多个别名,别名之间可以通过空格、逗号、分号等分隔( name=“user1 user2,user3;user4”)
<bean id="user" class="com.han.pojo.User" name="user1 user2,user3;user4">
<constructor-arg type="java.lang.String" value="张三"/>
</bean>
测试类中就可以使用user1这个别名获取bean
public class SpringTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
User user=context.getBean("user1", User.class);
System.out.println(user.toString());
}
}
五.spring中多配置文件的合并
import(多个配置文件合并,用于协作开发)
假设现在项目由两个人开发,产生了两个配置文件,beans.xml和beans2.xml:
上下文可以读取多个配置文件
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml","beans2.xml");
但一般不会这么做,而是合并到总的配置文件ApplicationContext 中:
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
注意:import对于配置文件的合并,会将不同配置文件中相同的代码合并,只保留一份。
六.spring的依赖注入
接下来,说一下spring依赖注入的三种方式
构造器注入
在之前的开发中,dao-daoImpl-service-serviceImpl
package com.han.pojo;
public interface UserDao {
void getUser();
}
package com.han.pojo;
public class UserDaoImpl implements UserDao {
public void getUser() {
System.out.println("默认获取用户数据");
}
}
package com.han.service;
public interface UserService {
void getUser();
}
package com.han.service;
import com.han.pojo.UserDao;
import com.han.pojo.UserDaoImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao=new UserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
@Test
public void testDependency(){
UserService userService=new UserServiceImpl();
userService.getUser();
}
没啥问题!但现在想要增加一个实现类
package com.han.pojo;
public class MysqlUserDaoImpl implements UserDao {
public void getUser() {
System.out.println("mysql获取用户数据");
}
}
则service层的实现类就要修改
package com.han.service;
import com.han.pojo.UserDao;
import com.han.pojo.UserDaoImpl;
public class UserServiceImpl implements UserService{
private UserDao userDao=new MysqlUserDaoImpl();
public void getUser() {
userDao.getUser();
}
}
所以每次增加一个实现类,service层实现类都必须修改
private UserDao userDao=new XXXUserDaoImpl();
现在,设置一个构造器,将上面的一句代码拆分为
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
测试类就变成这样了。
@Test
public void testDependency(){
UserService userService=new UserServiceImpl();
((UserServiceImpl)userService).setUserDao(new MysqlUserDaoImpl());
userService.getUser();
}
只需要每次通过set设置一个实现类方式即可,service层无需更改代码。控制权从程序员手中到了用户手中,这也是前面说的控制反转IOC的原型。在spring中实现控制反转的是IOC容器,实现方式是依赖注入。
但这里还是new了一个对象UserService userService=new UserServiceImpl();
。现在将类注入spring容器
<bean id="mysqlImpl" class="com.han.pojo.MysqlUserDaoImpl"/>
<bean id="UserServiceImpl" class="com.han.service.UserServiceImpl">
<property name="userDao" ref="mysqlImpl"/>
</bean>
这是测试类就不需要再new UserServiceImpl这个对象,而是上下文获取
@Test
public void testDependency(){
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserServiceImpl userService=context.getBean("UserServiceImpl",UserServiceImpl.class);
userService.getUser();
}
在这种方式下,想要更改实现类,只需要在配置文件中修改ref的值<property name="userDao" ref="XXXImpl"/>
set注入
bean对象的创建依赖于容器,对象的属性由容器注入。
下面创建实体类Student,测试多种数据类型的属性注入:
package com.han.pojo;
import java.util.*;
public class Student {
private String name; //基本类型属性
private Address address;//属性是一个对象的情况
private String[] books; //数组属性
private List<String> hobbys; //集合属性
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties 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> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
Address实体类注入到spring容器
//引用类型内部的属性,要在自己内部完成值的注入
<bean id="address" class="com.han.pojo.Address">
<property name="address" value="地球"/>
</bean>
Student这个Bean的属性值注入:
<bean id="address" class="com.han.pojo.Address">
<property name="address" value="地球"/>
</bean>
<bean id="student" class="com.han.pojo.Student">
<property name="name" value="张三"/> //1、基本类型
<property name="address" ref="address"/> //2、引用类型
<property name="books"> //3、数组
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<property name="hobbys"> //4、集合list
<list>
<value>唱歌</value>
<value>看电影</value>
<value>打游戏</value>
</list>
</property>
<property name="card"> //4、万能map,key-value键值对
<map>
<entry key="身份证" value="123456789"/>
<entry key="银行卡" value="778480569"/>
</map>
</property>
<property name="games"> //4、集合set
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!-- <property name="wife" value=""/>--> //设置空串,注意和null的区别
<property name="wife"> //5、null值
<null></null>
</property>
<property name="info"> //6、配置信息
<props>
<prop key="学号">2016211857</prop>
<prop key="姓名">张三</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
测试
@Test
public void SetTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student.toString());
}
拓展方式注入(p名空间、c命名空间)
建立Teacher实体类用于测试
package com.han.pojo;
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
为了代码简洁,新建一个配置文件CPnameSpace.xml,并导入到ApplicationContext.xml中,因为我们在测试类中,上下文读取的是总配置文件。
<import resource="CPnameSpace.xml"/>
下面在CPnameSpace.xml中,导入CP命名空间的支持(必须先导入支持才能使用)
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
注入Teacher这个bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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">
</beans>
之前的写法:
<bean id="teacher" class="com.han.pojo.Teacher">
<property name="name" value="张三"/>
<property name="age " value="20"/>
</bean>
使用p命名空间:
<bean id="teacher" class="com.han.pojo.Teacher" p:name="张三" p:age="20" />
测试
@Test
public void PTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Teacher teacher= context.getBean("teacher", Teacher.class);
System.out.println(teacher);
}
使用c命名空间
c命名空间,对应构造器注入,所以必须给Teacher实体类加上有参构造;而有参构造会覆盖无参构造,这样p命名空间对应set注入又会报错,所以需要同时加上有参和无参构造。
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
<bean id="teacher2" class="com.han.pojo.Teacher" c:name="李四" c:age="22"/>
测试
@Test
public void CTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Teacher teacher= context.getBean("teacher2", Teacher.class);
System.out.println(teacher);
}
七.Bean的自动装配—使用配置文件(重要!)
创建测试实体类
package com.han.pojo;
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
package com.han.pojo;
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
package com.han.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 "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
创建配置文件
<?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.han.pojo.Cat"/>
<bean id="dog" class="com.han.pojo.Dog"/>
<bean id="people" class="com.han.pojo.People">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
测试
public class AutoBeanTest {
@Test
public void test(){
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
People people=context.getBean("people",People.class);
people.getDog().shout();
people.getCat().shout();
}
}
到此,和之前的步骤都是一样的。现在使用ByName的自动装配方式。只给people这个bean注入name的属性,cat和dog的属性通过自动装配(autowire=“byName”):
<bean id="people" class="com.han.pojo.People" autowire="byName">
<property name="name" value="张三"/>
</bean>
byName:实体类People有两个属性cat和dog,对应的set方法setDog和setCat。People这个bean在装配的时候,在上文中寻找和自己的set方法后面的值相同的bean(dog、cat),不好理解。也就是寻找和自己的这两个属性同名的id对应的bean。
所以,如果这里的id不是cat或者dog,就无法被装配。
<bean id="cat1" class="com.han.pojo.Cat"/>
<bean id="dog1" class="com.han.pojo.Dog"/>
下面这一中方式,通过类型自动装配( autowire=“byType”)
<bean id="people" class="com.han.pojo.People" autowire="byType">
<property name="name" value="张三"/>
</bean>
byType:实体类People有两个属性cat和dog,它们是Cat和Dog类型。因此在这种自动装配方式下,会在上下文寻找class为Cat或者Dog的bean。
所以,如果此时同时存在两个class类型相同的bean,就无法自动装配。
<bean id="dog" class="com.han.pojo.Dog"/>
<bean id="dog1" class="com.han.pojo.Dog"/>
也就是说,byType必须保证bean的类型全局唯一。在byType的方式下,bean的id可以省略。
八.Bean的自动装配—使用注解(重要!)
要使用注解,需要导入context约束和aop支持
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
还要配置注解的支持
<context:annotation-config/>
也就是下面这样:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<bean id="cat" class="com.han.pojo.Cat"/>
<bean id="dog" class="com.han.pojo.Dog"/>
<bean id="people" class="com.han.pojo.People"/>
</beans>
此时,这三个bean的注入就是这样了
<bean id="cat" class="com.han.pojo.Cat"/>
<bean id="dog" class="com.han.pojo.Dog"/>
<bean id="people" class="com.han.pojo.People"/>
最后在People中使用@Autowired注解即可
@Autowired
private Cat cat;
@Autowired
private Dog dog;
这种方式,要求bean的id必须对应实体类的属性名,和byName相同。Autowired注解可以使用在属性或者set方法上使用,使用Autowired可以不写set方法,前提是自动装配的属性在spring容器中存在,且id符合byName
@Autowired(required = false)
private Cat cat;
Autowired注解可以加上required属性,为false表明这个对象可以为NULL,默认为true,不能为空。
public People(@Nullable String name) {
this.name = name;
}
也可以给属性加上@Nullable注解,此时属性为空也不会报空指针异常。
Autowired注解需要遵守byName,现在所有的bean的id都无法匹配到,此时可以结合注解@Autowired的value指定到一个bean,让value=这个bean的id即可自动装配。
<bean id="cat1" class="com.han.pojo.Cat"/>
<bean id="cat2" class="com.han.pojo.Cat"/>
<bean id="dog1" class="com.han.pojo.Dog"/>
<bean id="dog2" class="com.han.pojo.Dog"/>
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
一般Autowired和Qualifier配合使用。
还可以通过javax.annotation的原生注解实现自动装配@Resource
@Resource
private Cat cat;
@Resource
private Dog dog;
Resource会先根据id进行上下文查找
<bean id="cat" class="com.han.pojo.Cat"/>
<bean id="cat2" class="com.han.pojo.Cat"/>
<bean id="dog" class="com.han.pojo.Dog"/>
<bean id="dog2" class="com.han.pojo.Dog"/>
只要有一个id对应的上(cat、dog),就能自动装配。如果id没有一个可以匹配
<bean id="cat1" class="com.han.pojo.Cat"/>
<bean id="cat2" class="com.han.pojo.Cat"/>
<bean id="dog1" class="com.han.pojo.Dog"/>
<bean id="dog2" class="com.han.pojo.Dog"/>
Resource就会根据class类型去寻找bean,此时要求类型要全局唯一,遵守byType。除此之外,Resource也支持加上name属性,对应唯一bena。
此时id无法对应:
<bean id="cat1" class="com.han.pojo.Cat"/>
<bean id="cat2" class="com.han.pojo.Cat"/>
<bean id="dog1" class="com.han.pojo.Dog"/>
<bean id="dog2" class="com.han.pojo.Dog"/>
但是指定了name属性:
@Resource(name="cat1")
private Cat cat;
@Resource(name="dog1")
private Dog dog;
这样也能实现自动装配。
@Autowired默认使用byName自动装配,且要求属性在容器中存在;
@Resource默认先使用byName,找不到再根据byType装配,都找不到才会报错。
九.spring使用注解开发
使用注解开发需要注意(参见8):
必须导入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:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
</beans>
还有重要的一步要做,我们在每个bean中写的注解,得需要spring扫描后才会生效,通过包扫描的方式(至于扫描哪个层级的包需要视情况而定),这些注解是spring特有的注解,不能像@Autowired那样直接使用,需要spring扫描:
<context:component-scan base-package="com.han.pojo"/>
至此,就可以使用注解开发了。
建立一个实体类User
package com.han.pojo;
public class User {
public String name="张三";
}
这是一个User的bean,按照之前的做法,我们需要在配置文件中注入这个bean
<bean id="user" class="com.han.pojo.User"/>
但现在我们使用注解@Component加在User这个bean上:
package com.han.pojo;
import org.springframework.stereotype.Component;
@Component
public class User {
public String name="张三";
}
只需要在User这个bean上面加上@Component注解,就已经将这个bean注入到了spring容器中,不需要再使用bean标签显式的注册。
结论:@Component 等价于 <bean id="user" class="com.han.pojo.User"/>
测试:
注意,因为不再使用原来的bean标签注入容器,也就不存在id了。在通过context获取bean的时候,这里的"user"是实体类User的小写。也就是说,使用注解开发,实体类的小写就是默认的该实体类的bean的"id"。
public class AnnotationTest {
@Test
public void test(){
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user=context.getBean("user",User.class);
System.out.println(user.name);
}
}
不适用bean注入容器,也就没有了id。此时bean的属性的值的注入
public String name="张三";
该例是直接通过java语言赋初始值。可以通过@Value注解为属性赋值:
@Component
public class User {
@Value("张三")
public String name;
}
此外,@Value也可以放在属性的set方法上面:
@Component
public class User {
public String name;
@Value("张三")
public void setName(String name) {
this.name = name;
}
}
结论:@Value(“张三”)等价于<property name="name" value="张三"/>
对于一些简单的、个别的 属性通过@Value会非常方便,但如果属性非常多,当然建议使用配置为文件。
@Component有几个衍生注解,它们的本质还是@Component,只不过在分层开发模式下,代表特有的层级:
@Repository表示当前为dao层(mapper层)
@Repository
public class UserDao {
}
@Service表示当前为service层
@Service
public class UserService {
}
@Controller表示当前为controller层
@Controller
public class UserController {
}
注解开发下,bean作用域使用注解@Scope加在bean上,可以通过value属性为bean指定作用域:
@Component
@Scope(value = "singleton/prototype")
public class User {
@Value("张三")
public String name;
}
十.基于javaConfig实现配置
到目前位置,所有的例子都使用了配置文件,但支持完全不适用配置文件的全注解开发的,就是使用javaConfig实现配置,JavaConfig是spring的一个子项目。
新建一个实体类User
使用Component注解将这个bean注入到容器中,并给name属性赋初始值:
@Component
public class User {
@Value("张三")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
新建config文件夹,建立JavaConfig配置类(相当于之前的xml配置文件)
package com.han.config;
import com.han.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
public User getUser(){
return new User();
}
}
@Configuration:表示这个类是配置类,相当于xml配置文件,本质上还是@Component,是一个bean,会被spring接管到容器;
@Bean:相当于xml配置文件中的bean标签,会注册一个bean到容器中;
@getUser:方法名是bean的"id";
@User:返回值类型是bean的"class";
@return new User():返回值就是创建的对象;
测试:
public class JavaConfigTest {
@Test
public void test(){
ApplicationContext context=new AnnotationConfigApplicationContext(JavaConfig.class);
User user=context.getBean("getUser",User.class);
System.out.println(user.getName());
}
}
不再是之前的XML获取上下文ClassPathXmlApplicationContext
:
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
而是annotation获取上下文AnnotationConfigApplicationContext
获取的也不再是一个配置文件ApplicationContext.xml
,
而是上面写的配置类JavaConfig.class
:
ApplicationContext context=new AnnotationConfigApplicationContext(JavaConfig.class);
获取bean时,配置类中@Bean下的方法名getUser就是bean的"id";
如果需要扫描包,就在配置类中@Configuration这个注解下面加上@ComponentScan("com.han.pojo")
@Configuration
@ComponentScan("com.han.pojo")
public class JavaConfig {
@Bean
public User getUser(){
return new User();
}
}
我们知道,使用xml配置文件时,如果多个配置文件,可以通过import合并到总的配置文件中,上下文读取总的配置文件即可;现在使用注解开发,同样可以合并多个配置类到总的配置类中.通过@Import实现:
现在增加一个配置类,JavaConfigT
package com.han.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfigT {
}
通过@Import@Import(JavaConfigT.class)
导入到总配置类里面;
@Configuration
@ComponentScan("com.han.pojo")
@Import(JavaConfigT.class)
public class JavaConfig {
@Bean
public User getUser(){
return new User();
}
}
十一.AOP—横切事务(重要!)
静态代理—>动态代理(proxy、InvocationHandler)—>Spring AOP
使用spring的AOP,需要先导入织入依赖包:
<!-- springAOP织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
建立业务模型和实现类:
package com.han.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
package com.han.service;
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 select() {
System.out.println("查询一个用户");
}
}
第一种方式,使用spring原生的API接口。
现在,UserService有增删改查四种业务,想要通过AOP向UserService中横向切入增加日志功能Log。
新建Log实体类(通过前置增强的方式切入):
package com.han.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//method:要执行的目标对象的方法;
//args:参数;
//target:目标对象;
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
新建实体类AfterLog,通过后置增强的方式切入:
package com.han.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);
}
}
接下来,在ApplicationContext配置文件中,注入真实类UserService和两个日志增强类,并加入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 id="userService" class="com.han.service.UserServiceImpl"/>
<bean id="log" class="com.han.log.Log"/>
<bean id="afterLog" class="com.han.log.AfterLog"/>
<!-- 配置aop-->
<aop:config>
<!-- 切入点-->
<!-- pointcut:切入点-->
<!-- execution: 执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* com.han.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
第二种方式,自定义切面和切入点。
定义一个切面DiyPointCut :
package com.han.diy;
public class DiyPointCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
配置aop:
<bean id="diy" class="com.han.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref 要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="point" expression="execution(* com.han.service.UserServiceImpl.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
第三种方式,使用注解实现AOP
自定义一个切面annotationPointCut
package com.han.diy;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//@Component //将切面注册到spring容器
@Aspect //表明这是个切面
public class annotationPointCut {
@Before("execution(* com.han.service.UserServiceImpl.*(..))") //注解设置前置增强及切入点
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.han.service.UserServiceImpl.*(..))") //注解设置后置增强及切入点
public void after(){
System.out.println("方法执行后");
}
}
在配置文件中加入支持
<!-- 将切面注入到spring容器,或者在切面中使用@Component注解-->
<bean id="annotationPointCut" class="com.han.diy.annotationPointCut"/>
<!-- 使用注解的方式需要增加支持:自动代理-->
<aop:aspectj-autoproxy/>
测试
public class AopTest {
@Test
public void test(){
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
// 动态代理的是接口
UserService userService=context.getBean("userService", UserService.class);
userService.add();
}
}
可以看到,虽然真实业务类中只执行了add方法,但由于横切织入了日志增强类,于是日志内容也输出了。
下面测试环绕增强Around(ProceedingJoinPoint jp)可以给方法指定一个参数,表明执行的具体方法:
@Around("execution(* com.han.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("环绕前");
Signature signature=jp.getSignature();//获得签名:执行的方法
System.out.println(signature);
Object proceed=jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
可以看到,先执行环绕前,然后进入前置增强,结束环绕增强,最后进入后置增强。签名就是执行的方法。
以上就是AOP的三种实现方式,以及几种增强方式。
十二.spring整合mybatis
Mybatis能够无缝整合到srping中。spring整合mybatis需要导入两个依赖包
<!--spring整合mybtis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
spring整合mybatis后,数据源就使用spring的数据源,因此需要导入jdbc依赖:
<!--srping-jdbc:使用spring的数据源代替mybatis的数据源,需要导入这个包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
上面这两个包,是spring整合mybatis必须要用到。整合需要的包如下:
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--连接数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--srping-jdbc:使用spring的数据源代替mybatis的数据源,需要导入这个包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--aop织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring整合mybtis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
此外,为了保证配置文件正常加载,需要导入spring静态资源过滤的配置:
<!--静态资源过滤-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
下面,不直接给出所有整合代码,而是看看spring是如何接管mybatis的。
建立实体类User
package com.han.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
建立一个接口类Usermapper:
package com.han.mapper;
import com.han.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
建立接口实现的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.han.mapper.UserMapper">
<!--获取所有用户-->
<select id="selectUser" resultType="User">
select * from User;
</select>
</mapper>
建立数据库信息配置文件db.properties:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=root
建立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"/>
<!--扫描实体类:接口xml配置文件中就可以不用写全路径-->
<typeAliases>
<package name="com.han.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.han.mapper.UserMapper"/>
</mappers>
</configuration>
测试
public class MyTest {
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession=sqlSessionFactory.openSession(true);
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
List<User> userList=mapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
注意上面的例子是mybatis的使用,还没有牵扯到整合。
现在开始整合。
建立spring-dao.xml,这是spring整合mybatis的配置文件,也就是spring的配置文件。这个文件中,只配置三个东西:
1.数据源DateSource
2.SqlSessionFactory
3.创建sqlSessionTemplate对象
<?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">
<!-- 1.数据源DateSource:使用spring的数据源替换mybatis的配置-->
<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=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--2.SqlSessionFactory:获取与数据库的连接-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!--绑定mybatis核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--所有接口xml配置文件的注册都可以放在这里-->
<property name="mapperLocations" value="classpath:com/han/mapper/*.xml"/>
</bean>
<!-- 3.创建sqlSessionTemplate对象:它就是mybatis里面的sqlsession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--sqlSessionFactory这个属性的注入只能通过构造器注入,因为没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
在这个配置文件中,spring来管理数据源,管理xml接口配置文件,创建和数据库的连接。于是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"/>-->
<!--扫描实体类:接口xml配置文件中就可以不用写全路径-->
<typeAliases>
<package name="com.han.pojo"/>
</typeAliases>
<!--环境:spring接管数据源后,这部分就可以不要了-->
<!-- <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>-->
<!--注册所有的接口配置文件:已经映射到了spring-dao.xml,可以省略-->
<!-- <mappers>-->
<!-- <mapper class="com.han.mapper.UserMapper"/>-->
<!-- </mappers>-->
</configuration>
但是,spring整合mybatis后,mybatis对于接口的实现,spring是无法做到,因此就每个XXXMapper.xml就需要建立对应的XXXMapperImpl实现类。这里需要给Usermapper接口创建对应的实现UsermapperImpl:
package com.han.mapper;
import com.han.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
//SqlSessionTemplate:就是sqlSessionFactory
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
// 获取所有用户
public List<User> selectUser() {
UserMapper mapper= sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
现在,需要把这个实现类注入到容器:
注意:这个实现类可以写在spring-dao.xml里面。但是,可以发现spring-dao.xml这个配置文件里面都是关于数据源的配置,如果放在这里面,每次新建实现类,都要注入在这里面,这是不合适的。spring的多配置文件import合并的作用就来了。新建ApplicationContext.xml这个总的配置文件,将spring-dao.xml导入进去,同时将每个实现类注入进去。
这样spring-dao.xml的内容就固定了,只放数据源的配置。而且后面再整合SpringMvc的时候,也可以将SpringMvc的配置文件也导入到总的配置文件。
<!--注入接口实现类-->
<bean id="userMapper" class="com.han.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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整合mybatis的配置文件-->
<import resource="spring-dao.xml"/>
<!--注入接口实现类-->
<bean id="userMapper" class="com.han.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
到这里就完成了整合,下面测试,整合后就要使用spring的测试了:
public class MyTest {
@Test
public void test() throws IOException {
// Mybatis测试
// String resource = "mybatis-config.xml";
// InputStream inputStream = Resources.getResourceAsStream(resource);
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// SqlSession sqlSession=sqlSessionFactory.openSession(true);
//
// UserMapper mapper=sqlSession.getMapper(UserMapper.class);
// List<User> userList=mapper.selectUser();
// for (User user : userList) {
// System.out.println(user);
// }
//整合后,就要使用spring的测试
ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapper userMapper=context.getBean("userMapper",UserMapper.class);
List<User> userList=userMapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}
十三.Spring整合Myabtis的第二种方式
在第一种方式的基础上,稍作改动。
1、其他文件不变,将UserMapperImpl做如下修改:
原来的UserMapperImpl:
package com.han.mapper;
import com.han.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl implements UserMapper {
//SqlSessionTemplate:就是sqlSessionFactory
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
// 获取所有用户
public List<User> selectUser() {
UserMapper mapper= sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
现在的UserMapperImpl:
package com.han.mapper;
import com.han.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
//整合Mybatis的第二种方式
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
// 获取所有用户
public List<User> selectUser() {
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
通过继承SqlSessionDaoSupport类,省去下面的set方法和sqlSession的组合。
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
2、实现类注入到spring的改变
原来:
<!--注入接口实现类-->
<bean id="userMapper" class="com.han.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
现在:
<!--注入接口实现类-->
<bean id="userMapper" class="com.han.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
完成。
十四.spring的声明式事务(AOP的方式)
事务ACID原则:要么都成功,要么都失败。
- 原子性
- 一致性
- 隔离性
- 持久性
在原来的spring整合号mybatis的基础上,在userMapper的接口中增加两个方法:
public interface UserMapper {
public List<User> selectUser();
// 添加一个用户
public int addUser(User user);
//根据删除一个用户
public int deleteUser(int id);
}
在UserMapper.xml中写入sql:
<mapper namespace="com.han.mapper.UserMapper">
<!--获取所有用户-->
<select id="selectUser" resultType="User">
select * from User;
</select>
<insert id="addUser" parameterType="User">
insert into user(id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id=#{id}
</delete>
</mapper>
在UserMapperImpl实现类中实现这两个方法,并在selectUser中做测试:
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
// 获取所有用户
public List<User> selectUser() {
User userT=new User(7,"王二麻子","1234321");
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
mapper.addUser(userT);
mapper.deleteUser(6);
return mapper.selectUser();
}
@Override
public int addUser(User user) {
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
return mapper.addUser(user);
}
@Override
public int deleteUser(int id) {
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
return mapper.deleteUser(id);
}
}
注意:这里故意将delete写成deletes,使程序发生错误。
<delete id="deleteUser" parameterType="int">
deletes from user where id=#{id}
</delete>
测试:
程序报deletes的错误,但此时数据库还是成功插入了User userT=new User(7,"王二麻子","1234321");
这条数据。
这是不符合事务ACID原则的。
下面采用声明式事务进行处理,使程序报错的情况下,既然无法删除,也无法插入。
即要么都成功,要么都失败。
在spring-dao.xml配置文件中加入aop事务的配置:
<!--1.配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!--2.结合aop实现事务的织入-->
<!--配置事务通知:需要先导入tx支持-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--propagation:配置事务的传播特性,默认是REQUIRED(如果当前没有事务,就新建一个事务);
name="*":所有方法都支持事务;
name="add":只对add这个方法支持事务-->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3.配置事务切入-->
<aop:config>
<!--execution(* com.han.mapper.*.*(..)):com.han.mapper这个包下的所有类的所有方法的所有参数,都切入事务-->
<aop:pointcut id="txPointCut" expression="execution(* com.han.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
tx支持:
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
再次测试,程序依然是deletes报错,但此时由于配置了声明式事务,User userT=new User(7,"王二麻子","1234321");
这条数据并没有插入到数据库中。
将deletes改为delete:
删除id=6的用户,插入id=7的新用户
public List<User> selectUser() {
User userT=new User(7,"王二麻子","1234321");
UserMapper mapper= getSqlSession().getMapper(UserMapper.class);
mapper.addUser(userT);
mapper.deleteUser(6);
return mapper.selectUser();
}
成功,说明声明式事务已经在起作用了。
完!