概述
1、Spring
1.1、简介
spring框架以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版
spring理念:使现有的技术更加容易使用
maven的包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--与mybatis进行整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.2、优点
- spring是一个开源的免费的容器(框架)
- spring是一个轻量级的、非入侵式(引入spring不改变其他代码的情况)的框架
- 控制反转(IOC)面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
1.3、扩展
spring boot
- 一个快速开发的脚手架
- 基于springboot可以快速的开发单个微服务
- 约定大于配置
spring cloud
- springcloud是基于springboot实现的
现在大多数公司都在使用springboot进行快速开发,学习springboot的前提需要完全掌握spring以及springMVC
2、IOC
2.1、理论
在以前的案例中,我们编写程序的时候,会编写dao类和service类
但是之前的写法是直接在service中的成员属性里面,直接将dao类写死,以后如果更改业务那么就需要修改service层的源代码,这样就很不方便,于是我们在service类的代码中加入一个set方法,用来给dao属性进行赋值,在测试类中,如果我们需要更改业务,只需要使用set方法进行赋值,不需要更改源代码,这样就将对象的管理交给了程序自己来进行赋值,这就叫控制反转
UserDao
package com.hty.dao;
public interface UserDao {
public void getUser();
}
UserDaoImpl类
package com.hty.dao;
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据");
}
}
UserMysqlDaoImpl类
package com.hty.dao;
public class UserMysqlDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("mysql获取用户数据");
}
}
UserService接口
package com.hty.service;
public interface UserService {
public void getUser();
}
UserServiceImpl类
package com.hty.service;
import com.hty.dao.UserDao;
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
test类
import com.hty.dao.UserDao;
import com.hty.dao.UserDaoImpl;
import com.hty.service.UserService;
import com.hty.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
userService.getUser();
}
}
这种思想,从本质上解决了问题,我们程序员不需要去管理对象的创建,系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC的原型
3、第一个spring程序
需要在resources下创建一个beans.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用spring创建对象 在spring中这些都称为bean
bean = 对象 这个标签相当于是 new Hello();
id就是变量名
class 就是new的对象
property相当于给对象中的属性设置一个值
ref就是引用spring已经创建好的对象
-->
<bean id="hello" class="com.hty.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
实体类
package com.hty.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
//这个set方法必须有,因为spring就是考set方法注入属性的
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + ''' +
'}';
}
}
Test类
import com.hty.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//所有的对象都是在spring中管理,我们只需要取出里面的对象就行 参数就是xml文件中bean标签的id
//这样写Hello hello = context.getBean("hello",Hello.class);就不需要强转了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);
}
}
4、IOC创建对象的方式
在getBean方法执行的时候就创建了对象,默认采用的是无参构造创建对象
使用有参构造创建对象
方式一 使用下标
<bean id="user" class="com.hty.pojo.User">
<constructor-arg index="0" value="你好"/>
</bean>
方式二 使用类型来进行赋值 但是有局限性
<bean id="user" class="com.hty.pojo.User">
<constructor-arg type="java.lang.String" value="你好"/>
</bean
方式三 直接使用属性名进行赋值
<bean id="user" class="com.hty.pojo.User">
<constructor-arg name="name" value="你好"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
5、Spring配置
5.1、别名(alias标签)
<alias name="user" alias="userpro"/>
name就是在已经在spring中注册了的类的id
alias就是这个类的别名
5.2、Bean的配置(bean标签)
<bean id="user" class="com.hty,pojo.User" name="user2 u2,u3"></bean>
id :bean的唯一标识符,也就是相当于对象名
class :bean对象所对应的全限定名 包名+类型
name :也是别名 而且name更高级 可以同时取多个别名 分割符可以为逗号也可以为空格
5.3、import标签
一般用于团队开发使用,它可以将多个-配置文件导入合并为一个
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
6、依赖注入(DI)
6.1、构造器注入
前面已经写了
6.2、set方式注入(重点)
依赖注入:本质上是使用set方法注入
实体类
package com.hty.pojo;
import java.util.*;
//请自行生成get和set方法
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private Properties info;
private String wife;
}
package com.hty.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
beans.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.hty.pojo.Address">
<property name="address" value="陕西"/>
</bean>
<bean id="student" class="com.hty.pojo.Student">
<!--普通注入 value-->
<property name="name" value="张三"/>
<!--注入自定义对象 ref指向另一个bean的id-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>数学</value>
<value>语文</value>
<value>英语</value>
</array>
</property>
<!--list集合注入-->
<property name="hobby">
<list>
<value>打篮球</value>
<value>听歌</value>
<value>学习</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="1" value="1"/>
<entry key="2" value="2"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>cf</value>
<value>lol</value>
<value>csgo</value>
</set>
</property>
<!--null属性注入-->
<property name="wife">
<null/>
</property>
<!--properties配置文件注入-->
<property name="info">
<props>
<prop key="1">1</prop>
<prop key="2">2</prop>
</props>
</property>
</bean>
</beans>
6.3、拓展方式注入
6.3.1、p命名空间注入
需要在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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值-->
<bean id="user" class="com.hty.pojo.User" p:name="张三" p:age="18"/>
</beans>
该命名空间注入的方法类似于set注入
6.3.2、c命名空间注入
需要在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"
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">
<!--c命名空间注入-->
<bean id="user" class="com.hty.pojo.User" c:name="张三" c:age="18"/>
</beans>
该命名空间注入的方法类似于构造器注入注入
6.4、bean的作用域
6.4.1、单例模式
由于使用的是单例模式,所以全局同一个对象只有一个
import com.hty.pojo.Student;
import com.hty.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
User user1 = context.getBean("user",User.class);
User user2 = context.getBean("user",User.class);
System.out.println(user1 == user2);//true
}
}
从这个测试中就可以看出来,是单例模式
在bean标签中加入scope属性可以设置作用域 默认就是单例模式
<!--单例模式-->
<bean id="user" class="com.hty.pojo.User" c:name="张三" c:age="18" scope="singleton"/>
6.4.2、原型模式
每一个bean都会创建一个单独的对象
<!--原型模式-->
<bean id="user" class="com.hty.pojo.User" c:name="张三" c:age="18" scope="prototype"/>
6.4.3、其他的作用域
其余的request、session、application这些只能在web开发中使用到
7、Bean的自动装配
自动装配是spring满足bean依赖的一种方式,spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种装配方式
- 在xm中显示配置
- 在java中显示配置
- 隐式的自动装配bean
使用bean中的autowire属性来设置自动注入
基础环境
实体类
package com.hty.pojo;
public class Cat {
public void shout(){
System.out.println("猫叫");
}
}
package com.hty.pojo;
public class Dog {
public void shout(){
System.out.println("狗叫");
}
}
package com.hty.pojo;
public class People {
private Cat cat;
private Dog dog;
private String names;
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 getNames() {
return names;
}
public void setNames(String names) {
this.names = names;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", names='" + names + ''' +
'}';
}
}
7.1、byName自动装配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.hty.pojo.Cat"/>
<bean id="dog" class="com.hty.pojo.Dog"/>
<bean id="people" class="com.hty.pojo.People" autowire="byName">
<property name="names" value="张三"/>
</bean>
</beans>
这里我们并没有使用属性注入的方式注入cat和dog属性,而是让其自动装配
byName会自动在容器上下文中查找,和自己对象set方法后面的值对应的id,例如setCat方法就寻找的是cat相关的bean
7.2、byType自动装配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cat" class="com.hty.pojo.Cat"/>
<!--即使这里的名称不一样 在下面的bean中也能自动装配-->
<bean id="dog222" class="com.hty.pojo.Dog"/>
<bean id="people" class="com.hty.pojo.People" autowire="byType">
<property name="names" value="张三"/>
</bean>
</beans>
byType会自动在容器上下文中查找,和自己属性类型相同的bean的id,我们甚至可以不去设置有些类的id
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.hty.pojo.Cat"/>
<!--即使这里的名称不一样 在下面的bean中也能自动装配-->
<bean class="com.hty.pojo.Dog"/>
<bean id="people" class="com.hty.pojo.People" autowire="byType">
<property name="names" value="张三"/>
</bean>
</beans>
7.3、使用注解实现自动装配
jdk1.5才支持注解,Spring2.5开始支持注解
使用注解的话需要在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
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>
7.3.1、@Autowired
在实体类的属性上直接使用注解**@Autowired**就可以让spring自动在配置文件中寻找属性值,还可以在set方法上写,使用注解实现之后,我们可以不需要写set方法了
package com.hty.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String names;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getNames() {
return names;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", names='" + names + ''' +
'}';
}
}
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null
public People(@Nullable String name){...}
另一种方法就是
@Autowired(required = false)也可以实现上面的功能
required属性默认为true,意思是不能为null
我们还可以在@Autowired注解下面再跟一个@Qualifier注解
@Autowired
@Qualifier(value="dog")
private Dog dog;
这样就可以指定将哪一个bean赋值给这个属性,这样就可以防止bean中注册的id与属性名不符的情况
7.3.2、@Resource
java中也有一个注解可以实现@Autowired功能 @Resource 但是没有spring的高级
@Resource(name = "cat")
private Cat cat;
7.3.3、@Autowired和@Resource的异同
- 都是用来自动装配的,都可以放在字段上
- @Autowired通过byType的方式实现,而且必须要求这个对象存在(常用)
- @Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现,如果两个都找不到的情况下就会报错
- 执行顺序不同:@Autowired通过byTame的方式实现 @Resource默认通过byName的方式实现
8、使用完全注解开发
在spring之后,要使用注解开发,必须保证aop的包导入了,使用注解需要导入context约束,增加注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--指定要扫描的包 这个包下的注解就会生效-->
<context:component-scan base-package="com.hty"/>
<context:annotation-config/>
</beans>
使用注解开发之后,就不需要在spring配置文件中写这个类的配置了
8.1、@Component bean管理
这个组件放在类的上面,代表当前类被spring管理了,也就相当于是bean
//等价于 <bean id="user" class="com.hty.pojo.User"/>
@Component
public class User {}
8.2、@Value(值) 注入属性
用来给属性进行赋值
@Value("张三")
public String name;
8.3、@Component衍生的注解
@Component有几个衍生注解,我们在web开发中会按照mvc三层架构分层
- dao @Repository
- service @Service
- controller(servlet) @Controller
这些注解和@Component的本质是一样的,但是为了以后区分起来方便,所以有了这三个注解
8.4、bean的作用域
@Scope
使用这个注解可以设置是单例模式还是原型模式
@Scope("singleton")
public class User {}
9、小结
xml与注解:
- xml更加万能,适用于任何场合,维护简单方便
- 注解不是自己类使用不了,维护相对复杂
xml与注解使用位置:
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解支持
10、使用java配置spring
我们现在可以完全不使用spring 的xml配置,完全交给java来做
JavaConfig是spring的一个子项目,在spring4之后称为了一个核心功能
10.1、@Configuration
需要使用@Configuration注解来完成,代表他是一个配置类,我们需要一个MyConfig配置类
import com.hty.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
/*
这里的方法名就是bean标签的id属性
返回值就是bean中的class属性
*/
@Bean
public User getUser(){
return new User();
}
}
使用这种方式配置spring那么调用的方式也不同,我们使用的是Application的AnnotationConfigApplicationContext实现类
import com.hty.comfig.MyConfig;
import com.hty.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User getUser = (User) context.getBean("getUser");
System.out.println(getUser.getName());
}
}
实体类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
@Value("张三")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Configuration这个注解也是使用了@Component注解,所以也会被spring容器托管,注册到容器中
10.2、@ComponentScan(“包名”)
这个注解类似于xml中的
<!--指定要扫描的包 这个包下的注解就会生效-->
<context:component-scan base-package="com.hty"/>
可以去扫描包
10.3、@Import
这个注解可以将多个配置类集成在一起
@Configuration
@Import(MyConfig2.class)
public class MyConfig {}
需要传入的是另一个配置类的字节码对象
11、代理模式
代理模式的分类
- 静态代理
- 动态代理
11.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理后我们一般会做一些附属操作
- 客户:访问代理对象的人
案例:房东要出租房子,中介帮忙出租,用户不用去直接接触房东
//租房操作
public interface Rent {
public void rent();
}
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//中介
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//中介带你看房、帮房东出租、收中介费、签合同
@Override
public void rent() {
seeHouse();
host.rent();
hetong();
cost();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房");
}
//收中介费
public void cost(){
System.out.println("收中介费");
}
//签合同
public void hetong(){
System.out.println("签合同");
}
}
//客户
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理角色 中介帮房东出租 但是代理角色会有一些附属操作
Proxy proxy = new Proxy(host);
//不用面对房东,直接找中介租房就行
proxy.rent();
}
}
11.2、代理模式的好处与缺点
好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量就会翻倍,开发效率会变低
11.3、AOP出现原因
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rFKBGWT9-1645517456039)(spring笔记.assets/image-20220221181646205.png)]
11.3、动态代理
动态代理和静态代理的代理角色一样
动态代理的代理类是动态生成的,不是直接写好的
动态代理分类:
- 基于接口的动态代理—JDK代理
- 基于类的动态代理—cglib
- java字节码实现
需要了解两个类:Proxy(代理),InvocationHandle(调用处理程序)
InvocationHandler接口
InvocationHandler是由代理实例的调用处理程序实现的接口 。每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke
方法。
这个接口中只有一个方法就是invoke方法
Object invoke(Object proxy, 方法 method, Object[] args)
处理代理实例上的方法调用并返回结果。
参数含义
proxy - 调用该方法的代理实例
method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
Proxy类
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
这个就相当于是最后生成的代理类,使用这个类可以执行我们被代理对象的方法
静态方法
static InvocationHandler getInvocationHandler(Object proxy)
返回指定代理实例的调用处理程序。
static 类<?> getProxyClass(ClassLoader loader, 类<?>... interfaces)
给出类加载器和接口数组的代理类的 java.lang.Class对象。
static boolean isProxyClass(类<?> cl)
如果且仅当使用 getProxyClass方法或 newProxyInstance方法将指定的类动态生成为代理类时,则返回true。
static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
案例
//被代理的接口
public interface Rent {
public void rent();
}
//代理类
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//用和这个类自动生成代理类
public class ProxyInvocationHandle implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}
//生成得到代理类 写法是固定的,按照这个方法写就可以了
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果 args是被代理的方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现
seeHouse();
Object result = method.invoke(rent, args);
cost();
return result;
}
public void seeHouse(){
System.out.println("中介带你看房子");
}
public void cost(){
System.out.println("收中介费");
}
}
12、AOP
12.1、AOP简介
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范式。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skA0OIxz-1645517456040)(spring笔记.assets/image-20220221195627479.png)]
12.2、AOP在spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能,即使与我们业务逻辑无关,但是我们需要关注的部分就是横切关注点,如日志、安全、缓存、事务等
- 切面(ASPECT):横切关注点 被模块化的特殊对象,即,他是一个类
- 通知(Advice):切面必须要完成的工作,即,他是类中的一个方法
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 切入点(PointCut):切面通知执行的“地点”的定义
- 连接点(JointPoint):与切入点匹配的执行点
12.3、AOP中Advice的类型
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中添加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
即AOP在不改变原有代码的情况下,去添加新的功能
12.4、AOP实现
需要导入一个包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
使用的时候还需要加入命名空间
12.3.1、方式一:使用spring的api接口
案例
spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.hty.service.UserServiceImpl"></bean>
<bean id="beforeLog" class="com.hty.log.BeforeLog"></bean>
<bean id="afterLog" class="com.hty.log.AfterLog"></bean>
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点 expression表达式 execution(要执行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.hty.service.UserServiceImpl.*(..))"/>
<!--执行环绕增强-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
需要被代理的接口
//实现CRUD
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
接口的实现类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
前置通知类(其实就是在被代理的方法前执行的方法)
package com.hty.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//前置通知
public class BeforeLog implements MethodBeforeAdvice {
/*
* 参数说明
* method:要执行的目标对象方法
* Object:参数
* o:目标对象
* */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
后置通知类(其实就是在被代理的方法后执行的方法)
package com.hty.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//后置通知
public class AfterLog implements AfterReturningAdvice {
/*
* returnValue 返回值
* */
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);
}
}
测试类
import com.hty.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 = (UserService) context.getBean("userService");
userService.add();
}
}
**注意:**代理的是接口,所以获取的bean一定要是接口
12.3.2、execution表达式
执行(* com.sample.service.impl .. *。*(..))
符号 | 含义 |
---|---|
执行() | 表达式的主体 |
第一个” *“符号 | 表示返回值的类型任意 |
com.sample.service.impl | AOP所切的服务的包名,即,我们的业务部分 |
包名后面的” …“ | 表示当前包及子包 |
第二个” *“ | 表示类名,*即所有类。此处可以自定义,下文有举例 |
。*(…) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
12.3.3、方式二:自定义类实现
自定义的增强类
public class DiyPointCut {
public void before(){
System.out.println("============方法执行前============");
}
public void after(){
System.out.println("============方法执行后============");
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.hty.service.UserServiceImpl"></bean>
<bean id="beforeLog" class="com.hty.log.BeforeLog"></bean>
<bean id="afterLog" class="com.hty.log.AfterLog"></bean>
<!--方式二:自定义类-->
<!--首先注册自定义类-->
<bean id="diy" class="com.hty.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面 ref是要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.hty.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
12.3.4、方式三:使用注解实现
增强类
package com.hty.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
//方式三:使用注解方式实现AOP
@Aspect//标注这个类是一个切面
public class AnnotationPointcut {
@Before("execution(* com.hty.service.UserServiceImpl..*(..))")
public void before(){
System.out.println("============方法执行前============");
}
@After("execution(* com.hty.service.UserServiceImpl..*(..))")
public void after(){
System.out.println("============方法执行后============");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入点
@Around("execution(* com.hty.service.UserServiceImpl..*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//扩展:
//获得签名
// Signature signature = jp.getSignature();
// System.out.println("signature:"+signature);
//执行方法
jp.proceed();
System.out.println("环绕后");
}
}
需要在xml文件中开启一些配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="userService" class="com.hty.service.UserServiceImpl"></bean>
<bean id="beforeLog" class="com.hty.log.BeforeLog"></bean>
<bean id="afterLog" class="com.hty.log.AfterLog"></bean>
<!--方式三-->
<!--注册类-->
<bean id="annotationPointCut" class="com.hty.diy.AnnotationPointcut"/>
<!--开启注解支持
动态代理方式的设置 JDK(默认 proxy-target-class="false") cglib(proxy-target-class="true")
-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>
**注:**环绕通知的前置通知会在普通前置通知前面执行,环绕通知的后置通知会在普通后置通知前执行
13、整合Mybatis
步骤:
-
导入jar包
- junit
- mybatis
- mysql-connector
- spring相关的
- aop
- mybatis-spring(new)
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.14</version> </dependency> <!--spring操作数据库还需要 spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.14</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> </dependencies>
-
编写配置文件
-
测试
13.1、方式一
-
编写数据源配置
<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/mybatis?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
-
整合mybatis配置文件
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--mybatis中的注册mapper--> <property name="mapperLocations" value="classpath:com/hty/mapper/*.xml"/> <!--起别名--> <property name="typeAliasesPackage" value="com.hty.pojo"/> <!--绑定mybatis配置文件 这一步可以不要 只需要在spring中配置mybatis的所有--> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean>
-
创建SqlSessionTemplate SqlSessionTemplate就是mybatis中的sqlsession
<!--SqlSessionTemplate就是我们使用的SqlSession 只能使用构造器注入,因为没有set方法 --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
-
编写Mapper接口的实现类,因为bean只能创建类对象
import com.hty.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { //原来我们的所有操作都是用SqlSession来执行,现在都是用的是SqlSessionTemplate private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } @Override public List<User> selectUser() { return sqlSessionTemplate.getMapper(UserMapper.class).selectUser(); } }
将上述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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--DataSource:使用spring的数据源替换mybatis的配置 c3p0 dbcp druid
我们使用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/mybatis?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件 这一步可以不要 只需要在spring中配置mybatis的所有-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--mybatis中的注册mapper-->
<property name="mapperLocations" value="classpath:com/hty/mapper/*.xml"/>
<!--起别名-->
<property name="typeAliasesPackage" value="com.hty.pojo"/>
</bean>
<!--SqlSessionTemplate就是我们使用的SqlSession
只能使用构造器注入,因为没有set方法
-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
之后再将这个xml继承在总的spring配置文件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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring-dao.xml"/>
<!--注册mapper实现类,这样才能得到对象-->
<bean id="userMapperImpl" class="com.hty.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
</beans>
创建测试类进行测试
import com.hty.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserMapperImpl userMapperImpl = context.getBean("userMapperImpl", UserMapperImpl.class);
List<User> users = userMapperImpl.selectUser();
for (User user : users) {
System.out.println(user);
}
}
}
13.2、方式二
与方式一的大体步骤相同,只是在获取sqlSession的方式上有所区别,方式一是需要使用SqlSessionTemplate来获取sqlSessionTemplate,sqlSessionTemplate就完全相当于是sqlSession,而且需要在spring配置文件中注册bean,用set方式进行注入
方式二只需要让实现类继承一个类SqlSessionDaoSupport,并且在applicationContext.xml中给这个类注入sqlSessionFactory属性,之后直接使用getSqlSession方法就可以得到SqlSession对象了
import com.hty.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring-dao.xml"/>
<!--注意:虽然实现类中没有自定义属性,但是需要注入这个属性-->
<bean id="userMapperImpl" class="com.hty.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
14、声明式事务
13.1、回顾事务
事务特性
- 把一组业务当成一个业务来做,要么都成功,要么都失败
- 事务在项目开发中,十分重要,涉及到数据的一致性问题
- 确保完整性和一致性
事务的ACID原则
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中
13.2、spring的事务
- 声明式事务:AOP
- 编程式事务:需要在代码中进行事务的管理
步骤
在spring配置文件中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--DataSource:使用spring的数据源替换mybatis的配置 c3p0 dbcp druid
我们使用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/mybatis?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件 这一步可以不要 只需要在spring中配置mybatis的所有-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--mybatis中的注册mapper 使用和这个方式就可以注册这个包下的所有mapper-->
<property name="mapperLocations" value="classpath:com/hty/mapper/*.xml"/>
<!--起别名-->
<property name="typeAliasesPackage" value="com.hty.pojo"/>
</bean>
<!--SqlSessionTemplate就是我们使用的SqlSession
只能使用构造器注入,因为没有set方法
-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
<!--另一种写法-->
<!--<property name="dataSource" ref="dataSource"/>-->
</bean>
<!--结合AOP实现事务的植入-->
<!--配置事务通知(就是一个类 spring已经写好了)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性 propagation 默认是REQUIRED-->
<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="pointcut" expression="execution(* com.hty.mapper.*.*(..))"/>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
注:记得导入命名空间 aop和tx的命名空间
我们现在模拟一个错误来检测事务功能是否正常,首先创建好mapper环境
package com.hty.mapper;
import com.hty.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
//添加一个用户
public int addUser(User user);
//删除一个用户
public int deleteUser(int id);
}
package com.hty.mapper;
import com.hty.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user = new User(5, "张三", "123123");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(5);
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
<?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.hty.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
<insert id="addUser" parameterType="user">
insert into user(id,name,pwd) value(#{id},#{name},#{pwd});
</insert>
<delete id="deleteUser" parameterType="_integer">
delete from user where id=#{id};
</delete>
</mapper>
我们在实现类里面让selectUser方法中调用addUser和deleteUser,结果应该是增加一个数据然后删除,如果我们将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.hty.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
<insert id="addUser" parameterType="user">
insert into user(id,name,pwd) value(#{id},#{name},#{pwd});
</insert>
<delete id="deleteUser" parameterType="_integer">
<!--这里的delete关键字多加了一个s-->
deletes from user where id=#{id};
</delete>
</mapper>
最后我们发现数据库中没有插入数据,此时就表示我们的事务配置好了
异常处理
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userMapper' is expected to be of type 'com.hty.mapper.UserMapperImpl' but was actually of type 'com.sun.proxy.$Proxy10'
这个异常出现的原因就是没有加入配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
只需要将这个配置添加在aop标签的下面就可以解决问题了
r(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
```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.hty.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
<insert id="addUser" parameterType="user">
insert into user(id,name,pwd) value(#{id},#{name},#{pwd});
</insert>
<delete id="deleteUser" parameterType="_integer">
delete from user where id=#{id};
</delete>
</mapper>
我们在实现类里面让selectUser方法中调用addUser和deleteUser,结果应该是增加一个数据然后删除,如果我们将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.hty.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
<insert id="addUser" parameterType="user">
insert into user(id,name,pwd) value(#{id},#{name},#{pwd});
</insert>
<delete id="deleteUser" parameterType="_integer">
<!--这里的delete关键字多加了一个s-->
deletes from user where id=#{id};
</delete>
</mapper>
最后我们发现数据库中没有插入数据,此时就表示我们的事务配置好了
异常处理
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userMapper' is expected to be of type 'com.hty.mapper.UserMapperImpl' but was actually of type 'com.sun.proxy.$Proxy10'
这个异常出现的原因就是没有加入配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
只需要将这个配置添加在aop标签的下面就可以解决问题了
最后
以上就是高高大船为你收集整理的spring狂神说笔记1、Spring2、IOC3、第一个spring程序4、IOC创建对象的方式5、Spring配置6、依赖注入(DI)7、Bean的自动装配8、使用完全注解开发9、小结10、使用java配置spring11、代理模式12、AOP13、整合Mybatis14、声明式事务的全部内容,希望文章能够帮你解决spring狂神说笔记1、Spring2、IOC3、第一个spring程序4、IOC创建对象的方式5、Spring配置6、依赖注入(DI)7、Bean的自动装配8、使用完全注解开发9、小结10、使用java配置spring11、代理模式12、AOP13、整合Mybatis14、声明式事务所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复