Spring简介

  • SSH : Struct2 + Spring + Hibernate!
  • SSm : SpringMVC + Spring + Mybatis!

Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。

引入maven

1
2
3
4
5
6
7
8
9
10
11
12
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version>
</dependency>

Spring的组成和拓展

组成

img

拓展

  • Spring Boot
    • 一个快速开发的脚手架
    • 基于Spring Boot可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud
    • Spring Cloud是基于Spring Boot实现的。

弊端:人称“配置地狱”。

IOC理论推导

控制反转思想

1
2
3
4
5
private UserDao userDao;
// 利用set动态进行值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

这种思想大大降低系统的耦合性,可以更加专注的在业务的实现上,这就是IOC的原型。

HelloSpring

IOC本质

container magic

xml配置骨架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>

IOC创建对象方式

默认无参构造

有参构造实现方式

1
2
3
4
<!--    直接通过参数名赋值-->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg name="name" value="mingchengfuzhi"/>
</bean>

总结

结论:在配置文件加载的时候。其中管理的对象都已经初始化了!

Spring配置说明

别名

1
2
<!--    别名 如果添加了别名 我们也可以用别名获取到这个对象-->
<alias name="user" alias="user2"/>

bean的配置

1
2
3
4
5
6
7
<!--
id bean的唯一标识符
class 包名+类型
name : 也是别名 而且可以取多个 分隔符多样
-->
<bean id="userT" class="com.kuang.pojo.UserT" name="userT2 userT3,userT4;userT5"/>

import

团队的合作通过import来实现

1
2
3
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

DI依赖注入环境

概念

  • 依赖注入(Dependency Injection,DI)。
  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

构造器注入

我们在之前的案例已经讲过了

Set 注入 (重点)

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没 有set方法 , 是 is .

测试pojo类 :

Address.java

1
2
3
4
5
6
7
8
9
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.kuang.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
* TODO
*
* @ClassName Student
* @Author Alfa
* @Data 2022/6/7 18:13
* @Version 1.0
**/
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 void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public void setGames(Set<String> games) {
this.games = games;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
public void show(){
System.out.println("name="+ name
+ ",address="+ address.getAddress()
+ ",books="
);
for (String book:books){
System.out.print("<<"+book+">>\t");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("wife:"+wife);
System.out.println("info:"+info);
}
}

常量注入

1
2
3
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
</bean>

Bean注入

1
2
3
4
5
6
7
<bean id="addr" class="com.kuang.pojo.Address">
<property name="address" value="重庆"/>
</bean>
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
</bean>

注意点:这里的值是一个引用,ref

数组注入

1
2
3
4
5
6
7
8
9
10
11
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</array>
</property>
</bean>

List注入

1
2
3
4
5
6
7
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>

Map注入

1
2
3
4
5
6
<property name="card">
<map>
<entry key="中国邮政" value="456456456465456"/>
<entry key="建设" value="1456682255511"/>
</map>
</property>

set注入

1
2
3
4
5
6
7
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>

Null注入

1
<property name="wife"><null/></property>

Properties注入

1
2
3
4
5
6
7
<property name="info">
<props>
<prop key="学号">20190604</prop>
<prop key="性别"></prop>
<prop key="姓名">小明</prop>
</props>
</property>

测试结果:

image-20220607185606372

p命名和c命名注入

P命名空间注入 : 需要在头文件中加入约束文件

1
2
3
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/>

c 命名空间注入 : 需要在头文件中加入约束文件

1
2
3
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="狂神" c:age="18"/>

把有参构造器加上,这里也能知道,c 就是所谓的构造器注入!

Bean的作用域

image-20220608090910144

单例模式

1
2
<!--    设置作用域为单例,默认模式-->
<bean id="user2" class="com.kuang.pojo.User" c:age="16" c:name="框线" scope="singleton"/>

原型模式

1
<bean id="user2" class="com.kuang.pojo.User" c:age="16" c:name="框线" scope="prototype"/>

每次从容其中get都会产生新对象

其余的request、session、application,这些只能在web开发中使用到。

自动装配Bean

  • 自动装配是Spring满足Bean依赖的一种方式
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;

  2. 在java中显式配置;

  3. 隐式的bean发现机制和自动装配。

    ByName自动装配

    1
    2
    3
    4
    5
    6
    <bean id="cat" class="com.kuang.pojo.Cat"/>
    <bean id="dog222" class="com.kuang.pojo.Dog"/>
    <!-- byName 会自动在容器上下文查找,和自己set方法后面的值对应的beanid-->
    <bean id="people" class="com.kuang.pojo.People" autowire="byName">
    <property name="name" value="狂神"/>
    </bean>

    ByType自动装配

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="cat" class="com.kuang.pojo.Cat"/>
    <bean id="dog222" class="com.kuang.pojo.Dog"/>
    <!-- byName 会自动在容器上下文查找,和自己set方法后面的值对应的beanid
    byType 会自动在容器上下文查找,类型相同的beanid
    -->
    <bean id="people" class="com.kuang.pojo.People" autowire="byType">
    <property name="name" value="狂神"/>
    </bean>
    • ByName时,需保证所有bean的id唯一
    • ByType时,需保证所有bean的类型唯一

使用注解自动装配

image-20220608092943699

要使用注解须知:

  1. 导入约束

    1
    xmlns:context="http://www.springframework.org/schema/context"
  2. 配置注解支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?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方法上

现在set方法可以省略,因为注解基于反射实现。前提是你这个自动装配的属性在Spring容器中存在,且符合ByName

科普

1
2
3
public @interface Autowired {
boolean required() default true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class People {
// 如果显示的说明required为false,说明这个属性可以为null
@Autowired(required = false)
private Cat cat;
@Autowired
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;
}
}

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用。
  1. 配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

    1
    2
    3
    4
    <bean id="dog1" class="com.kuang.pojo.Dog"/>
    <bean id="dog2" class="com.kuang.pojo.Dog"/>
    <bean id="cat1" class="com.kuang.pojo.Cat"/>
    <bean id="cat2" class="com.kuang.pojo.Cat"/>
  2. 在属性上添加Qualifier注解

    1
    2
    3
    4
    5
    6
    @Autowired
    @Qualifier(value = "cat2")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;

@Resource

  • 更高级,会自动ByName查询,之后进行类型自动注入,也可以通过那么属性指定bean的id
  • @Resource和@Autowired都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType的方式实现
  • @Resource默认通过byname实现,如果找不到名字,就通过byType的方式实现,如果两个都找不到,才报错。
  • @Resource和@Autowired执行顺序不同,@Autowired通过byType的方式实现

Spring注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

image-20220608144926351

在配置文件当中,还得要引入一个context约束

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

</beans>

属性注入

1
2
3
4
5
6
7
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("秦疆")
// 相当于配置文件中 <property name="name" value="秦疆"/>
public String name;
}

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
1
2
3
4
5
6
@Controller("user")
@Scope("prototype")
public class User {
@Value("秦疆")
public String name;
}

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入

使用JavaConfig实现配置

基于Java类进行配置

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版 本, JavaConfig 已正式成为 Spring4 的核心功能 。

新建一个config配置包,编写一个MyConfig配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.kuang.config;

import com.kuang.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;

/**
* TODO
*
* @ClassName Config
* @Author Alfa
* @Data 2022/6/8 15:17
* @Version 1.0
**/
//这个也会被注册到spring容器中,因为它本就是一个组件
//@Configuration是一个配置类,他就等同于之前的beans.xml
@Configuration
@ComponentScan("com.kuang.pojo")
@Import(KuangConfig2.class)
public class KuangConfig {
// 注册一个类,就相当于之前写的bean,
// bean标签中的id是这个方法的名字,
// 方法的返回值相当于bean标签的class属性
@Bean
public User getUser(){
return new User();
}
}

静态代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!

代理模式的分类:

  • 静态代理
  • 动态代理

image-20220608155118384

角色分析:

  • 抽象角色:一般会使用接口或者抽象类
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人

代码实现

Rent . java 即抽象角色

1
2
3
4
5
//抽象角色:租房
public interface Rent {
public void rent();
}

Host . java 即真实角色

1
2
3
4
5
6
7
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}

Proxy . java 即代理角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
//租房
public void rent(){
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}

Client . java 即客户

1
2
3
4
5
6
7
8
9
10
11
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}

代理模式的好处

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情
  • 公共的业务由代理来完成 . 实现了业务的分工
  • 公共业务发生扩展时变得更加集中和方便

缺点 :

一个类对应一个代理类,工作量翻倍,解决方法就是动态代理!

动态代理详解

  • 动态代理的角色和静态代理的一样 .
  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理–cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist

需要了解两个类

  1. Proxy :代理

    image-20220609103558776

  2. InvocationHandler:调用处理程序

    image-20220609105821550

万能模板

代理处理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.kuang.demo04;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* TODO
*
* @ClassName ProxyInvocationHandler
* @Author Alfa
* @Data 2022/6/9 10:37
* @Version 1.0
**/
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;

public void setTarget(Object target) {
this.target = target;
}

// public static Object newProxyInstance(ClassLoader loader,
// Class<?>[] interfaces,
// InvocationHandler h)
// 生成得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 处理代理实例并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}

public void log(String msg) {
System.out.println("执行了"+msg+"方法");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kuang.demo04;

import com.kuang.demo02.UserService;
import com.kuang.demo02.UserServiceImpl;

/**
* TODO
*
* @ClassName Client
* @Author Alfa
* @Data 2022/6/9 10:53
* @Version 1.0
**/
public class Client {
public static void main(String[] args) {
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色,不存在
ProxyInvocationHandler handler = new ProxyInvocationHandler();
// 设置要代理的对象
handler.setTarget(userService);
// 动态生成代理类
UserService proxy = (UserService) handler.getProxy();
proxy.delete();
}
}

AOP实现方式一

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序 功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要 内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各 部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

image-20220609110239109


以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注 的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。 连接点(JointPoint):与切入点匹配的执行点。

image-20220609110519485

使用Spring实现Aop

【重点】使用AOP织入,需要导入一个依赖包!

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>

第一种方式

首先编写我们的业务接口和实现类

1
2
3
4
5
6
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 search() {
System.out.println("查询用户");
}
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

1
2
3
4
5
6
7
8
9
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object
target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(*
com.kuang.service.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>

测试

1
2
3
4
5
6
7
8
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}

AOP实现方式二

第二种方式 自定义类来实现Aop

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

1
2
3
4
5
6
7
8
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}

去spring中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--第二种方式自定义实现-->
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二种方式:使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(*
com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>

AOP方式二从零实现

  1. 导入maven依赖

    1
    2
    3
    4
    5
    <!--AOP织入依赖-->
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    </dependency>
  2. 自定义切面类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.backstage.log;

    /**
    * 操作日志切面类
    *
    * @ClassName OperateLogAspect
    * @Author Alfa
    * @Data 2022/6/9 11:35
    * @Version 1.0
    **/
    public class OperateLogAspect {
    public void before() {
    System.out.println("==========before============");
    }

    public void after() {
    System.out.println("===========after=============");
    }
    }
  3. 配置applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?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.backstage.service.impl.UserServiceImpl"/>

    <!--自定义切面,ref要引用的类-->
    <bean id="logAspect" class="com.backstage.log.OperateLogAspect"/>
    <aop:config>
    <aop:aspect ref="logAspect">
    <!-- 切入点-->
    <aop:pointcut id="point" expression="execution(* com.backstage.service.impl.StaffServiceImpl.updatePassword(..))"/>
    <!-- 通知-->
    <aop:before method="before" pointcut-ref="point"/>
    <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
    </aop:config>
    </beans>
  4. 测试类

    1
    2
    3
    4
    5
    6
    @Test
    public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = (UserService) context.getBean("userService");
    userService.login(new User());
    }

注解实现AOP

第三种方式 使用注解实现

第一步:编写一个注解实现的增强类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

1
2
3
<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

注解方式从零实现

  1. 配置applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?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">
    <!--配置注解实现aop-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
  2. 切面类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * 操作日志切面类
    *
    * @ClassName OperateLogAspect
    * @Author Alfa
    * @Data 2022/6/9 11:35
    * @Version 1.0
    **/
    @Aspect
    @Component
    public class OperateLogAspect {
    @Around("execution(* com.backstage.service.impl.OrderFromServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pj) throws Throwable {
    //获取当前登录用户
    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    HttpSession session=attr.getRequest().getSession(true);
    Staff user = (Staff)session.getAttribute("staff");
    System.out.println(user);
    //获取方法所在包
    System.out.println("==========before============");
    String methodName = pj.getSignature().getName();
    System.out.println(methodName);
    Object o = pj.proceed();
    System.out.println(o);
    System.out.println("===========after=============");
    }
    }

回顾Mybatis

  1. 导入jar包

    • junit

    • mybatis

    • mysql数据库

    • spring相关的

    • aop织入

    • mybatis-spring

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.10.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.10.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <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.2</version>
    </dependency>
  2. 编写配置

    • 配置Maven静态资源过滤问题!

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <build>
      <resources>
      <resource>
      <directory>src/main/java</directory>
      <includes>
      <include>**/*.properties</include>
      <include>**/*.xml</include>
      </includes>
      <filtering>true</filtering>
      </resource>
      </resources>
      </build>
  3. 测试

整合mybatis方式一

mybatis-config.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
</configuration>

spring-dao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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">

<!-- 数据源DataSource:使用spring的数据源替换mybatis的配置-->
<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_plus?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</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/kuang/mapper/*.xml"/>
</bean>
<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

<bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>

</beans>

applicationContext.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring-dao.xml"/>
</beans>

测试

1
2
3
4
5
6
7
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userDao");
List<User> user = mapper.selectUser();
System.out.println(user);
}

整合mybatis方式二

mybatis-spring1.2.3版以上的才有这个 .

官方文档截图 :

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要 管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看

image-20220610125436970

实现步骤

  1. 将我们上面写的UserMapperImpl修改一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * TODO
    *
    * @ClassName UserMapperImpl2
    * @Author Alfa
    * @Data 2022/6/10 12:46
    * @Version 1.0
    **/
    public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> getAllUser() {
    UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
    return mapper.getAllUser();
    }
    }
  2. 配置bean

    1
    2
    3
    <bean id="userMapper2" class="com.kuang.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
  3. 测试

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void selectUser() throws IOException {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = (UserMapper) context.getBean("userMapper2");
    List<User> user = userMapper.getAllUser();
    System.out.println(user);
    }

Spring中的事务管理

声明式事务管理

  1. 使用Spring管理事务,注意头文件的约束导入 : tx

    1
    2
    3
    xmlns:tx="http://www.springframework.org/schema/tx"
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
  2. 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <!--    配置声明式事务-->
    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 结合AOP实现事务的织入-->
    <!-- 配置事务的类-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 给那些方法配置事务-->
    <!-- 配置事务的传播特性-->
    <tx:attributes>
    <tx:method name="add" propagation="REQUIRED"/>
    <tx:method name="delete" propagation="REQUIRED"/>
    <tx:method name="update" propagation="REQUIRED"/>
    <tx:method name="query" read-only="true"/>
    <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
    </tx:advice>

    <!-- 配置事务切入-->
    <aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.kuang.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>