1、Spring
1.1、简介
- Spring:春天——>给软件行业带来了春天!
- 2002,首次推出了Spring框架的雏形:interface21框架!
- Spring框架即以interface21框架为基础,经过重新设计.并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。
- Rod Johnson,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
- spring理念:使现有的技术更加容易使用,本实施一个大杂烩,整合了现有的技术框架!
- SSH:Struct2+Spring+Hibernate
- SSM:SpringMvc+Spring+Mybatis
官网:https://spring.io/projects/spring-framework#overview
官网下载地址:https://repo.spring.io/release/org/springframework/spring
GitHub:https://github.com/spring-projects/spring-framework
<!--https://mvnrepository.com/artifact/org.springframework/spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
1.2、优点
- Spring是一个开源的免费的框架(容器)!
- Spring是一个轻量级的,非入侵式的框架!
- 控制反转(IOC),面向切面编程(AOP)!
- 支持事务的处理,对框架整合的支持!
==总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!==
1.3、组成
1.4、拓展
在Spring的官网有这个介绍:现代化的java开发!说白就是基于Spring开发!
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速的开发单个微服务
- 约定大于配置!
- Spring Cloud
- SpringCloud是基于SpringBoot实现的
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!
弊端:发展了太久之后,违背了原来的历练理念!配置十分繁琐。人称:“配置地狱!”
2、IOC理论推导
1、userDao 接口
package com.xiaomin.dao;
/**
* @author 解你忧
* @date 2022/04/03 9:57
* @product_namae spring
* @product_namae IntelliJ IDEA
*/
public interface userDao {
void getUser();
}
2、userDaoImpl 实现类
package com.xiaomin.dao;
/**
* @author 解你忧
* @date 2022/04/03 9:58
* @product_namae spring
* @product_namae IntelliJ IDEA
*/
public class userDaoImpl implements userDao{
@Override
public void getUser() {
System.out.println("获取用户数据!");
}
}
3、userService 业务接口
package com.xiaomin.service;
/**
* @author 解你忧
* @date 2022/04/03 9:58
* @product_namae spring
* @product_namae IntelliJ IDEA
*/
public interface userService {
void getUser();
}
4、userServiceImpl 业务实现类
package com.xiaomin.service;
import com.xiaomin.dao.userDao;
/**
* @author 解你忧
* @date 2022/04/03 10:04
* @product_namae spring
* @product_namae IntelliJ IDEA
*/
public class userServiceImpl implements userService{
private userDao userDao;
//利用set进行动态实现值得注入!
public void setUserDao(userDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码!如果代码量十分大,修改一次的成本代价十分昂贵!
我们使用一个Set接口实现,已经发生了革命性的变化!
private userDao userDao;
//利用set进行动态实现值得注入!
public void setUserDao(userDao userDao) {
this.userDao = userDao;
}
- 之前,程序员主动创建对象!控制权在程序员手上!
- 使用了set注入后,程序员不在具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上!这是IOC的原型!
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3、HelloSpring
@Test
public void test01() {
//获取Spring的上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理了,我们要使用,直接去里面取出来就可以!
hello hello =(hello) context.getBean("hello");
System.out.println(hello.toString());
}
到了现在,我们彻底不用再程序中去改动了,要实现不同操作,只需要在xml配置文件中进行修改,所谓的IoC,一句话搞定:对象由Spring来创建,管理,装配!
4、IOC创建对象的方式
1、使用无参构造创建对象,默认!
2、假设我们要使用有参构造创建对象。
1、下标赋值
<!--第一种,下标赋值!-->
<bean id="user" class="com.xiaomin.pojo.user">
<constructor-arg index="0" type="java.lang.String" value="小敏"/>
</bean>
2、类型
<!--第二种方式:通过类型创建,不建议使用!-->
<bean id="user" class="com.xiaomin.pojo.user">
<constructor-arg type="java.lang.String" value="小敏"/>
</bean>
3、参数名
<!--第三种,直接通过参数名来设置-->
<bean id="user" class="com.xiaomin.pojo.user">
<constructor-arg name="name" value="小敏520"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
5、Spring配置
5.1、别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="xiaomin"/>
5.2、Bean的配置
<!--
id:bean的唯一标识符,也就是相当于我们学过的对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名,而且name可以去多个别名,并且可以使用空格,逗号,分号进行分割
-->
<bean id="userT" class="com.xiaomin.pojo.userT" name="user2 u2,u3;u4">
<property name="name" value="小敏"/>
</bean>
5.3、import
这个import,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个
假设,现在项目中有多个人开发。这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
张三
李四
王五
applicationContext.xml
```xml
使用的时候,直接用总的配置就可以了 # 6、依赖注入 ## 6.1、构造器注入 第四点有提到 ## 6.2、set方式注入【重点】 依赖注入:set注入! - 依赖:bean对象的创建依赖于容器 - 注入:bean对象中的所有属性,由容器来注入 【环境搭建】 1. 复杂类型 Address类 2. 真实测试对象 Student类 3. beans.xml 4. 测试 MyTest3 student类 ```java package com.xiaomin.pojo; import java.util.*; /** * @author 解你忧 * @date 2022/04/05 19:59 * @product_namae spring * @product_namae IntelliJ IDEA */ 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 com.xiaomin.pojo.address getAddress() { return address; } public void setAddress(com.xiaomin.pojo.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.toString() + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
address
package com.xiaomin.pojo;
/**
* @author 解你忧
* @date 2022/04/05 19:59
* @product_namae spring
* @product_namae IntelliJ IDEA
*/
public class address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "address{" +
"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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.xiaomin.pojo.address">
<property name="address" value="四川"/>
</bean>
<bean id="student" class="com.xiaomin.pojo.student">
<!--第一种,普通注入,value-->
<property name="name" value="小敏"/>
<!--第二种,Bean注入,ref-->
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>《红楼梦》</value>
<value>《西游记》</value>
<value>《水浒传》</value>
<value>《三国演义》</value>
</array>
</property>
<!--List-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!--Map-->
<property name="card">
<map>
<entry key="身份证" value="111111222222223333"></entry>
<entry key="银行卡" value="6222561616516656165"></entry>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="学号">2005021215</prop>
<prop key="性别">男</prop>
<prop key="姓名">小敏</prop>
</props>
</property>
</bean>
</beans>
test
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
student student =(student) context.getBean("student");
System.out.println(student.toString());
}
6.3、拓展注入
我们可以使用p命名空间和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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.xiaomin.pojo.user" p:name="小敏" p:age="15"/>
<!--p命名空间注入,可以通过构造器注入:constructs-args-->
<bean id="user2" class="com.xiaomin.pojo.user" c:_0="小艳" c:_1="20"/>
</beans>
测试:
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
user user = context.getBean("user", user.class);
System.out.println(user);
user user2 = context.getBean("user2", user.class);
System.out.println(user2);
}
注意点:p命名和c命名空间不能直接使用,需要导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4、bean的作用域
1.单例模式(Spring默认机制)
<bean id="user2" class="com.xiaomin.pojo.user" c:_0="小艳" c:_1="20" scope="singleton"/>
2.原型模式:每次从容器中get的时候,都会产生一个新的对象!
<bean id="user2" class="com.xiaomin.pojo.user" c:_0="小艳" c:_1="20" scope="prototype"/>
3.其余的request、session、application这些只能在web开发中使用到!
7、Bean的自动装配
- 自动装配是Spring满足bean依赖一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
- 在xml中显示的配置
- 在Java中显示的配置
- 隐式的自动装配bean【重要】
7.1、测试
环境搭建:一个人有两个宠物
7.2、ByName自动装配
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid!
-->
<bean id="people" class="com.xiaomin.pojo.People" autowire="byName">
<property name="name" value="小敏"/>
</bean>
7.3、ByType自动装配
<bean class="com.xiaomin.pojo.Cat"/>
<bean class="com.xiaomin.pojo.Dog"/>
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid!
byType:会自动在容器上下文中查找,和自己对象属性类型相同的beanid!
注意:byType 所需类型必须全局唯一,可以连id都不需要
-->
<bean id="people" class="com.xiaomin.pojo.People" autowire="byType">
<property name="name" value="小敏"/>
</bean>
小结:
- byname的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入属性的set方法的值一致!
- bytype的时候,需要保证所有有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
7.4、使用注解实现自动装配
jdk1.5支持的注解,Spring2.5就支持注解了!
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.
要使用注解须知:
导入约束。context约束
==配置注解的支持:context:annotation-config/==
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
@Autowired
直接在属性上使用即可!也可以在set方法上使用!
使用Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname!
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null;
public @interface Autowired { boolean required() default true; }
测试代码
//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name; }
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value=“xxx”)去配合@Autowired的使用,指定一个唯一的bean对象注入!
public class People {
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog22")
private Dog dog;
private String name;
}
@Resource注解 (我的jdk没有@Resource)
小结:
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过byType的方式实现,而且必须要求这个对象存在!【常用】
- @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
- 执行顺序不同:@Autowired 通过byType的方式实现。@Resource默认通过byname的方式实现。
8、使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入context约束,增加注解的支持!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"
>
<context:annotation-config/>
</beans>
bean
属性如何注入
@Component public class User { //相当于<property name="name" value="小敏"/> @Value("小敏") public String name; }
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
dao【@Repository】
service【@Service】
controller【@Controller】
这四个注解功能都是一样的。都是代表将某个类注册到Spring中,装配Bean
自动装配
@Autowired:自动装配通过类型,名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier("xxx") @Nullable 字段标记了这个注解,说明这个字段可以为null; @Resource:自动装配通过名字,类型。
作用域
@Component @Scope("prototype") public class User { //相当于<property name="name" value="小敏"/> @Value("小敏") public String name; }
小结
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己类使用不了,维护相对复杂!
xml与注解最佳实践
xml用来管理bean;
注解只负责完成属性的注入;
我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.xiaomin"/> <context:annotation-config/>
9、使用java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
实体类
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@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 + '\'' +
'}';
}
}
配置文件
package com.xiaomin.config;
import com.xiaomin.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
// 这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml是一样的
@Configuration
@ComponentScan("com.xiaomin.pojo")
@Import(MinConfig2.class)
public class MinConfig {
//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean("user")//可指定可不指定
public User getUser(){
return new User();//就是返回要注入到bean的对象!
}
}
测试类!
public class MyTest {
public static void main(String[] args) {
//如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载!
ApplicationContext context = new AnnotationConfigApplicationContext(MinConfig.class);
User getUser = (User) context.getBean("user");
System.out.println(getUser.getName());
}
}
这种纯Java的配置方式,在SpringBoot中随处可见!
10、代理模式
中介:为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
10.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人!
代码步骤:
接口
//租房 public interface Rent { public void rent(); }
真实角色
//房东 public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子"); } }
代理角色
@Data @NoArgsConstructor @AllArgsConstructor public class Proxy implements Rent { private Host host; @Override public void rent() { seeHouse(); host.rent(); hetong(); fare(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //签合同 public void hetong(){ System.out.println("签租赁合同"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
客户端访问代理角色
public class Client { public static void main(String[] args) { //房东要租房子 Host host=new Host(); //代理,中介帮房东租房子,但是呢?代理角色一般会有附属操作 Proxy proxy = new Proxy(host); //你不用面对房东,直接找中介租房即可 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更加纯粹!
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍
开发效率会变低
10.2、加深理解
UserService
public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
UserServiceImpl
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("查询了一个用户"); } //1.改动原有的业务代码,在公司中是大忌! }
UserServiceProxy
public class UserServiceProxy implements UserService{ private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //日志方法 public void log(String msg){ System.out.println("[Debug]使用了"+msg+"方法"); } }
Client
public class Client { public static void main(String[] args) { UserService userService=new UserServiceImpl(); UserServiceProxy userServiceProxy=new UserServiceProxy(); userServiceProxy.setUserService(userService); userServiceProxy.add(); } }
聊聊AOP
10.3、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 ··· JDK动态代理【我们在这里使用】
- 基于:cglib
- java字节码实现:javassist
需要了解两个类:Proxy:代理,InvocationHandler:调用处理程序
动态代理的好处:
- 可以使真实角色的操作更加纯粹!
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般都是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!
11、AOP
11.1、什么是AOP
11.2、AOP在Spring中的使用
==提供声明式事务,允许用户自定义切面==
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
- 切面(Aspect):横切关注点 被模块化的特殊对象。即,它是一个类。(Log类)
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。(Log类中的方法)
- 目标(Target):被通知对象。(生成的代理类)
- 代理(Proxy):向目标对象应用通知之后创建的对象。(生成的代理类)
- 切入点(PointCut):切面通知执行的”地点”的定义。(最后两点:在哪个地方执行,比如:method.invoke())
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即AOP在不改变原有代码的情况下,去增加新的功能。(代理)
11.3、使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring的API接口【主要SpringAPI接口实现】
applicationContext.xml
<!--方式一:使用原生Spring API接口-->
配置aop:需要导入aop的约束
<aop:config>
<!--切入点:expression:表达式,execution(要执行的位置!* * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.xiaomin.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
Log.java
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//object:参数
//target:目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
AfterLog.java
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);
}
}
方式二:自定义来实现AOP【主要是切面定义】
applicationContext.xml
<!--方式二:自定义类-->
<bean id="diy" class="com.xiaomin.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.xiaomin.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
<!--通知-->
</aop:aspect>
</aop:config>
DiyPointCut.java
public class DiyPointCut {
public void before(){
System.out.println("=========方法执行前=========");
}
public void after(){
System.out.println("=========方法执行后=========");
}
}
方式三:使用注解实现
applicationContext.xml
<!--方式三-->
<bean id="annotationPointCut" class="com.xiaomin.diy.AnnotationPointCut"/>
<!--开启注解支持! JDK(默认:proxy-target-class="false") cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy/>
AnnotationPointCut.java
//方式三:使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.xiaomin.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=====方法执行前=====");
}
@After("execution(* com.xiaomin.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.xiaomin.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Signature signature = jp.getSignature();//获得签名
System.out.println("signature:"+signature);
Object proceed = jp.proceed();//执行方法
System.out.println(proceed);
System.out.println("环绕后");
}
}
12、整合Mybatis
步骤:
- 导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop注入
- mybatis-spring【new】
- 编写配置文件
- 测试
12.1、回忆mybatis
- 编写实体类
- 编写核心配置文件
- 编写接口
- 编写Mapper.xml
- 测试
实例:
- 导入相关的jar,pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring</artifactId>
<groupId>xiaomin</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<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>5.1.47</version>-->
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作数据库的话,需要一个spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
<!--在build中配置resources,来防止资源导出失败的问题-->
<!-- Maven解决静态资源过滤问题 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>false</filtering>-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>true</filtering>-->
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
- 编写实体类,User.java
@Data
public class User {
private int id;
private String name;
private String password;
}
- 编写核心配置文件,resources/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 核心配置文件-->
<configuration>
<!--开启日志-->
<!--<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!--可以给实体类起别名 -->
<typeAliases>
<package name="com.xiaomin.pojo"/>
</typeAliases>
<!--environments 可配置多个环境-->
<environments default="development">
<!--多个环境,不同id-->
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/software2032?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="你自己的用户名"/>
<property name="password" value="你自己的密码"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册!!!-->
<mappers>
<mapper class="com.xiaomin.mapper.UserMapper"/>
<!--<mapper resource="com/xiaomin/mapper/UserMapper.xml"/>-->
</mappers>
</configuration>
- 编写接口,UserMapper.xml
public class UserMapperImpl implements UserMapper {
//我们的所有操作,在原来都使用sqlSession来执行,现在都使用SqlSessionTemplate;
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.selectUser();
}
}
- 编写接口配置文件,UserMapper.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.xiaomin.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user
</select>
</mapper>
- 测试,test/java/MyTest.java
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);
}
}
}
12.2、Mybatis-spring
- 编写数据源配置
- sqlSessionFactory
- sqlSessionTemplate(相当于sqISession)
- 需要给接口加实现类【】
- 将自己写的实现类注入到Spring中
- 测试使用即可
数据源:
- DataSource:使用Spring的数帮源替换Mybatis的配置
- 这使用Spring提供的JDBC:org.springframework.jdbc.datasource
- 其他数据源:c3p0、dbcp、druid
【核心对象】:
sqISessionFactory
在基础的 MyBatis 用法中,是通过
SqlSessionFactoryBuilder
来创建SqlSessionFactory
的。 而在 MyBatis-Spring 中,则使用SqlSessionFactoryBean
来创建。
sqISessionTemplate
SqlSessionTemplate
是 MyBatis-Spring 的核心。作为SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的SqlSession
。SqlSessionTemplate
是线程安全的,可以被多个 DAO 或映射器所共享使用。当调用 SQL 方法时(包括由
getMapper()
方法返回的映射器中的方法),SqlSessionTemplate
将会保证使用的SqlSession
与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的DataAccessExceptions
。由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用
SqlSessionTemplate
来替换 MyBatis 默认的DefaultSqlSession
实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。可以使用
SqlSessionFactory
作为构造方法的参数来创建SqlSessionTemplate
对象。
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定 Mybatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/xiaomin/mapper/*.xml"/>
</bean>
<!-- sqlSessionTemplate 就是之前使用的:sqlsession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory 原因:它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
先导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring</artifactId>
<groupId>xiaomin</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<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>5.1.47</version>-->
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Spring操作数据库的话,需要一个spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
<!--在build中配置resources,来防止资源导出失败的问题-->
<!-- Maven解决静态资源过滤问题 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>false</filtering>-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--<filtering>true</filtering>-->
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
编写顺序:
User -> UserMapper -> UserMapper.xml -> spring-dao.xml -> UserServiceImpl -> applicationContext.xml -> MyTest6
代码步骤:
pojo实体类 User
@Data
public class User {
private int id;
private String name;
private String password;
//Lombok生成的在我这里相当于没用,必须重写toString方法,否则输出的是地址
@Override
public String toString() {
return "User(" +
"id:" + id +
", name:'" + name + '\'' +
", password:'" + password + '\'' +
')';
}
}
接口UserMapper
public interface UserMapper {
public List<User> selectUser();
}
UserMapperImpl
public class UserMapperImpl implements UserMapper {
//我们的所有操作,在原来都使用sqlSession来执行,现在都使用SqlSessionTemplate;
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
return userMapper.selectUser();
}
}
UserMapper.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.xiaomin.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user
</select>
</mapper>
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 核心配置文件-->
<configuration>
<!--开启日志-->
<!--<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>-->
<!--可以给实体类起别名 -->
<typeAliases>
<package name="com.xiaomin.pojo"/>
</typeAliases>
<!--environments 可配置多个环境-->
<!--配置了Spring-Dao.xml后,这块可以直接注释掉了:dataSource
<environments default="development">
<!–多个环境,不同id–>
<environment id="development">
<!–事务管理–>
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!–<property name="driver" value="com.mysql.jdbc.Driver"/>–>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/software2032?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="XiaoMin"/>
<property name="password" value="990421lm"/>
</dataSource>
</environment>
</environments>-->
<!--每一个Mapper.xml都需要在MyBatis核心配置文件中注册!!!-->
<!-- 配置了Spring-Dao.xml后,这块可以直接注释掉了:绑定Mybatis配置文件
<mappers>
<mapper class="com.xiaomin.mapper.UserMapper"/>
<!–<mapper resource="com/xiaomin/mapper/UserMapper.xml"/>–>
</mappers>-->
</configuration>
spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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"
>
<!--DataSource:使用Spring的数帮源替换Mybatis的配置 其他数据源:c3p0、dbcp、druid
这使用Spring提供的JDBC: org.springframework.jdbc.datasource -->
<!--dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.cj.jdbc.Driver" />
<!--后面那片段必须加上,否则报错:The server time zone value '�й���ʱ��' is unrecognized or
represents more than one time zone-->
<property name="url" value="jdbc:mysql://localhost:3306/software2032?
useSSL=true&useUnicode=true&characterEncoding=UTF-8&
serverTimezone=GMT%2B8&autoReconnect=true"/>
<property name="username" value="XiaoMin" />
<property name="password" value="990421lm" />
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定 Mybatis 配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/xiaomin/mapper/*.xml"/>
</bean>
<!-- sqlSessionTemplate 就是之前使用的:sqlsession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory 原因:它没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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"
>
<!--导入其他配置文件-->
<import resource="spring-dao.xml"/>
<!--<import resource="spring-mvc.xml"/>-->
<!--mybatis-spring 方式1-->
<bean id="userMapper" class="com.xiaomin.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
测试类
public class MyTest {
@Test
public void test() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
13、声明式事务
1、回顾事务
- 把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分重要,涉及到数据的一致性问题,不能马虎!
- 确保完整性和一致性;
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化到存储器中!
2、spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中,进行事务的管理
思考:
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况下;
- 如果我们不再Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!