文章摘要
GPT 4

1.介绍

官网:https://spring.io/

Spring 让每个人都可以更快、更轻松、更安全地编写 Java。Spring 对速度、简单性和生产力的关注使其成为世界上最受欢迎的Java 框架。

我们使用了 Spring 框架附带的许多工具,并获得了许多开箱即用的解决方案所带来的好处,并且不必担心编写大量额外的代码——这确实为我们节省了一些时间和能量。

2.特征

  • 核心技术:依赖注入、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP。
  • 测试:模拟对象、TestContext 框架、Spring MVC 测试、WebTestClient.
  • 数据访问:事务、DAO 支持、JDBC、ORM、编组 XML。
  • Spring MVCSpring WebFlux Web 框架。
  • 集成:远程处理、JMS、JCA、JMX、电子邮件、任务、调度、缓存。
  • 语言:Kotlin、Groovy、动态语言。

3.核心技术

IOC:控制反转,将对象的创建权交给了Spring去管理

DI:依赖注入,把数据给创建好的对象中的属性进行赋值

AOP:面向切面编程,底层是代理模式

4.Bean的创建

org.springframework.beansorg.springframework.context包是 Spring Framework 的 IoC 容器的基础。该 BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContextBeanFactory的子接口。它补充说:

  • 更容易与 Spring 的 AOP 功能集成
  • 消息资源处理(用于国际化)
  • 活动发布
  • 应用层特定上下文,例如WebApplicationContext 用于 Web 应用程序的上下文。

org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。

4.1无参构造创建

导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- spring核心依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

创建User实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.coutrue.pojo;

/**
* User实体类
*/
public class User {

private Integer id;
private String name;
private String password;

public User() {
System.out.println("无参构造");
}
}

编写applicationContext.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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">

<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<bean id="user" class="com.coutrue.pojo.User">
</bean>

</beans>

编写测试类

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
package com.couture.test;

import com.couture.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* 测试类
*/
public class SpringTest {

@Test
public void testUser() {
//需要加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

//根据bean标签中的id属性值,获取bean对象
User user = (User)applicationContext.getBean("user");

System.out.println(user);

}
}

4.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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.couture.factory;

import com.couture.pojo.User;

/**
* 通过实例方法获取User对象
*/
public class UserFactory {

/**
* 获取 User 对象
* @return
*/
public User getUser() {
//return new User();

//反射 + 配置文件
try {
return (User)Class.forName("com.qf.pojo.User").newInstance();
} catch (Exception e) {
e.printStackTrace();
}

//return null;
//手动抛异常
throw new RuntimeException("创建User对象异常");
}


/**
* 通过静态方法获取 User 对象
* @return
*/
public static User getUserStatic() {
//return new User();

//反射 + 配置文件
try {
return (User)Class.forName("com.qf.pojo.User").newInstance();
} catch (Exception e) {
e.printStackTrace();
}

//return null;
//手动抛异常
throw new RuntimeException("创建User对象异常");
}
}

配置applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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">

<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<!-- <bean id = "user" class = "com.coutrue.pojo.User">-->
<!-- </bean>-->

<!-- 工厂中的实例方法创建Bean -->
<!-- <bean id = "userFactoryBean" class = "com.coutrue.factory.UserFactory"></bean>-->
<!-- <bean id = "user" factory-bean="userFactoryBean" factory-method="getUser"></bean>-->

<!-- 工厂中的静态方法创建Bean -->
<bean id="user" class="com.coutrue.factory.UserFactory" factory-method="getUserStatic"></bean>

</beans>

4.3简单工厂模式

Car

1
2
3
4
5
6
7
8
9
10
11
12
package com.couture.factorymode.simplefactory;

/**
* Car 接口
*/
public interface Car {

/**
* 提供 run 方法
*/
public void run();
}
1
2
3
4
5
6
7
8
9
10
11
package com.couture.factorymode.simplefactory;

/**
* Car 实现类
*/
public class BaoSJ implements Car {
@Override
public void run() {
System.out.println("保时捷在飞驰...");
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.couture.factorymode.simplefactory;

/**
* Car 的实现类
*/
public class FaLL implements Car {
@Override
public void run() {
System.out.println("法拉利在飞驰...");
}
}

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
package com.couture.factorymode.simplefactory;

/**
* 简单工厂模式:代码集中,不符合 OCP 原则( open - close ):对代码的扩展是开放的,对代码的修改是关闭的
* 车工厂
*/
public class CarFactory {

//提供创建保时捷的方法
public static BaoSJ creatBaoSJ() {
try {
return (BaoSJ)Class.forName("com.coutrue.factorymode.simplefactory.BaoSJ").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

//提供创建法拉利的方法
public static FaLL creatFaLL() {
try {
return (FaLL)Class.forName("com.coutrue.factorymode.simplefactory.FaLL").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.couture.factorymode.simplefactory;

/**
* 测试类
*/
public class Test {
public static void main(String[] args) {

//创建对象,调用方法
//BaoSJ baoSJ = new BaoSJ();
BaoSJ baoSJ = CarFactory.creatBaoSJ();
baoSJ.run();

//创建对象,调用方法
//FaLL faLL = new FaLL();
FaLL faLL = CarFactory.creatFaLL();
faLL.run();

}
}

4.4抽象工厂模式

Car

1
2
3
4
5
6
7
8
9
10
11
12
package com.couture.factorymode.abstractfactory;

/**
* Car 接口
*/
public interface Car {

/**
* 提供 run 方法
*/
public void run();
}
1
2
3
4
5
6
7
8
9
10
11
package com.couture.factorymode.abstractfactory;

/**
* Car 实现类
*/
public class BaoSJ implements Car {
@Override
public void run() {
System.out.println("保时捷在飞驰...");
}
}
1
2
3
4
5
6
7
8
9
10
11
package com.couture.factorymode.abstractfactory;

/**
* Car 的实现类
*/
public class FaLL implements Car {
@Override
public void run() {
System.out.println("法拉利在飞驰...");
}
}

CarFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.couture.factorymode.abstractfactory;

/**
* 抽象工厂模式:符合开闭原则
* 车工厂
*/
public interface CarFactory {

/**
* 创建车的方法
* @return
*/
public Car createCar();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.couture.factorymode.abstractfactory;

/**
* 保时捷的车工厂
*/
public class BaoSJFactory implements CarFactory{
/**
* 创建保时捷车
* @return
*/
@Override
public Car createCar() {
try {
return (BaoSJ)Class.forName("com.qf.factorymode.abstractfactory.BaoSJ").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.couture.factorymode.abstractfactory;

/**
* 法拉利车工厂
*/
public class FaLLFactory implements CarFactory{
@Override
public Car createCar() {
try {
return (FaLL)Class.forName("com.qf.factorymode.abstractfactory.FaLL").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.couture.factorymode.abstractfactory;

/**
* 测试类
*/
public class Test {
public static void main(String[] args) {

//创建对象,调用方法
BaoSJFactory baoSJFactory = new BaoSJFactory();
Car baoSJ = baoSJFactory.createCar();
baoSJ.run();

//创建对象,调用方法
FaLLFactory faLLFactory = new FaLLFactory();
Car faLL = faLLFactory.createCar();
faLL.run();

}
}

5.Bean的作用范围

5.1scope属性

bean标签的scope属性:指定当前bean的作用范围

取值:

singleton:单例( 默认值 )

prototype:多例

request:作用于Web应用的请求范围

session:作用于Web应用的会话范围

global-session:作用于集群环境Web应用的会话范围(全局会话)

1
2
3
4
5
6
7
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
scope:常用的是单例(singleton)和多例(prototype)
-->
<bean id = "user" class = "com.couture.pojo.User" scope="prototype">
</bean>
5.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
package com.couture.singletonmode;

/**
* 单例模式:懒汉式(在第一次调用的时候实例化自己)
* 优势:第一次调用才会初始化,避免内存消耗
* 劣势:必须加锁才能保证单例,加锁会影响效率
*/
public class SingletonLazy {

//构造器私有化
private SingletonLazy() {}

//声明对象
private static SingletonLazy singletonLazy = null;

//实例化,线程安全
public static synchronized SingletonLazy getSingletonLazy() {
//判断
if(null == singletonLazy) {
singletonLazy = new SingletonLazy();
}

return singletonLazy;
}

}
5.3单例模式-饿汉式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.couture.singletonmode;

/**
* 饿汉式:比较常用,容易产生垃圾( GC回收 )
* 优势:没有加锁,效率会提高
* 劣势:类加载时就进行初始化,消耗内存
*/
public class SingletonHungry {

//私有化构造器
private SingletonHungry() {}

//实例化
private static SingletonHungry singletonHungry = new SingletonHungry();

//方法
public static SingletonHungry getSingletonHungry() {
return singletonHungry;
}
}
5.4单例模式-双重校验锁
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
package com.couture.singletonmode;

/**
* 双重校验锁
* 优势:安全,在多线程情况下保证较高的性能
*/
public class SingletonLock {

//构造器私有化
private SingletonLock() {}

//声明
private static SingletonLock singletonLock = null;

//实例化方法
public static SingletonLock getSingletonLock() {
//先检查当前实例是否为空,如果不存在再进行同步
if(null == singletonLock){
//同步
synchronized (SingletonLock.class) {
//再次检查当前实例是否为空
if(null == singletonLock){
//返回
singletonLock = new SingletonLock();
}
}
}
return singletonLock;
}
}

6.Bean生命周期

1
2
3
4
5
6
7
8
9
10
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
scope:常用的是单例(singleton)和多例(prototype)
init-method:创建对象后执行的初始化方法
destroy-method:对象销毁后执行(如果是多例模式下不执行)
-->
<bean id = "user" class = "com.couture.pojo.User"
scope="singleton" init-method="initUser" destroy-method="destroyUser">
</bean>
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
package com.couture.pojo;

/**
* User实体类
*/
public class User {

private Integer id;
private String name;
private String password;

public User() {
System.out.println("无参构造");
}

/**
* 初始化方法,创建对象后执行
*/
public void initUser() {
System.out.println("User 初始化方法");
}

/**
* 销毁方法,销毁spring容器中对象后执行
*/
public void destroyUser() {
System.out.println("User 销毁方法");
}

}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 测试生命周期相关方法
*/
@Test
public void testLife() {
//创建ClassPathXmlApplicationContext对象
ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext("applicationContext.xml");

//获取对象
User user = (User) applicationContext.getBean("user");

System.out.println(user);

//关闭
applicationContext.close();
}

7.依赖注入

DI:Dependency Injection:给创建对象中的属性赋值

IOC作用:降低程序间耦合(依赖关系)

依赖关系维护:以后都交给Spring进行管理

可注入类型:

基本数据类型以及包装类

String类

类类型( 其他Bean类型 )

复杂类型:集合,数组…

7.1set方法注入

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
24
25
26
27
28
29
<?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">

<!-- 初始化一个Car对象 -->
<bean id="firstCar" class="com.couture.pojo.Car">
<property name="cid" value="20001"></property>
<property name="cname" value="保时捷"></property>
</bean>
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<bean id = "user" class = "com.coutrue.pojo.User">
<!-- set方法赋值
property表示当前对象的属性
name:属性名
value:给当前属性赋值
ref:用于注入其他Bean对象(在spring容器中已经创建了)
-->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="password" value="123"></property>
<property name="car" ref="firstCar"></property>
</bean>

</beans>

实体类

1
2
3
4
5
6
7
8
9
10
11
package com.couture.pojo;

import lombok.Data;

@Data
public class Car {

private Integer cid;
private String cname;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.couture.pojo;

import lombok.Data;

@Data
public class User {

private Integer id;
private String name;
private String password;

private Car car;
}

测试

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.couture.test;

import com.qf.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* 测试类
*/
public class SpringTest {

@Test
public void testUser(){

//加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

//创建Bean
User user =(User)applicationContext.getBean("user");

//输出
System.out.println(user);
}
}

7.2复杂类型注入

1.注入数组对应的标签:array

2.注入LIst以及Set集合的标签:list,set

3.注入Map以及Propertis的标签:map,properties

创建实体类CollectionVo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.couture.vo;

import lombok.Data;

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

/**
* 数组以及集合类型的属性注入
*/
@Data
public class CollectionVo {

private Integer [] arr;
private List list;
private Set set;
private Map map;
private Properties properties;

}

在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
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
<!-- 复杂类型注入 -->
<bean id="collectionVo" class="com.coutrue.vo.CollectionVo">
<!-- 数组 -->
<property name="arr">
<array>
<value>123</value>
<value>456</value>
<value>789</value>
</array>
</property>

<!-- List -->
<property name="list">
<list>
<value>jack</value>
<value>jack</value>
<value>rose</value>
<ref bean="user"></ref>
</list>
</property>

<!-- Set -->
<property name="set">
<set>
<value>张三</value>
<value>张三</value>
<value>李四</value>
<ref bean="user"></ref>
</set>
</property>

<!-- Map -->
<property name="map">
<map>
<entry key="1001" value="张三"></entry>
<entry key-ref="user" value-ref="firstCar"></entry>
</map>
</property>

<!-- Properties -->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">root</prop>
<prop key="url">jdbc:mysql:///db_name?serverTimezone=Asia/Shanghai</prop>
<prop key="driverClassName">com.mysql.cj.jdbc.Driver</prop>
</props>
</property>

</bean>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 测试复杂类型注入
*/
@Test
public void testCollectionVo() {

//加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

//创建Bean
CollectionVo collectionVo = (CollectionVo) applicationContext.getBean("collectionVo");

//输出
System.out.println(collectionVo);
}

7.3构造器注入( 不常用 )

创建Car

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.couture.pojo;

import lombok.Data;

@Data
public class Car {

private Integer cid;
private String cname;

public Car(Integer cid, String cname) {
this.cid = cid;
this.cname = cname;
}
}

创建User

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
package com.couture.pojo;

import lombok.Data;

@Data
public class User {

private Integer id;
private String name;
private String password;

private Car car;

public User(Integer id, String name, String password, Car car) {
this.id = id;
this.name = name;
this.password = password;
this.car = car;

System.out.println("第一个构造器");
}

/**
* 把 id 和 name 交换了一下顺序
* @param name
* @param id
* @param password
* @param car
*/
public User(String name, Integer id,String password, Car car) {
this.id = id;
this.name = name;
this.password = password;
this.car = car;

System.out.println("第二个构造器");
}
}

配置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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?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">

<!-- 初始化一个Car对象,要和类中的构造器器匹配 -->
<!--
id和name用法相同
-->
<bean name="firstCar" class="com.couture.pojo.Car">
<!-- 构造器注入
name:获取构造器中指定参数的名称
value:给构造器中的参数赋值(基本数据类型以及String类型)
ref:给其他bean类型赋值
type:指定注入的类型
index:指定参数的位置,默认从0开始
-->
<constructor-arg name="cid" value="20001" type="java.lang.Integer" index="0"></constructor-arg>
<constructor-arg name="cname" value="保时捷" type="java.lang.String" index="1"></constructor-arg>
</bean>

<!-- 初始化一个User对象,通过改变 index 来设置通过使用第一个构造器创建对象 -->
<bean id = "user1" class = "com.couture.pojo.User">
<constructor-arg name="id" value="1001" index="0"></constructor-arg>
<constructor-arg name="name" value="张三" index="1"></constructor-arg>
<constructor-arg name="password" value="123" index="2"></constructor-arg>
<constructor-arg name="car" ref="firstCar" index="3"></constructor-arg>
</bean>

<!-- 初始化一个User对象,通过改变 index 来设置通过使用第二个构造器创建对象 -->
<bean id = "user2" class = "com.couture.pojo.User">
<constructor-arg name="id" value="1002" index="1"></constructor-arg>
<constructor-arg name="name" value="李四" index="0"></constructor-arg>
<constructor-arg name="password" value="456" index="2"></constructor-arg>
<constructor-arg name="car" ref="firstCar" index="3"></constructor-arg>
</bean>

</beans>

测试

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
package com.couture.test;

import com.couture.pojo.Car;
import com.couture.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

@Test
public void testCar() {

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

Car car = (Car) applicationContext.getBean("firstCar");

System.out.println(car);
}

@Test
public void testUser() {

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

User user1 = (User) applicationContext.getBean("user1");
System.out.println(user1);

User user2 = (User) applicationContext.getBean("user2");
System.out.println(user2);
}
}

7.4注解注入

注意:需要在applicationContext.xml文件中,导入context约束

Car

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
package com.couture.pojo;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
* @Component 将对象放到 spring 容器中,相当于:<bean id = "" class = "" />
* 如果一个注解的属性默认值是 value,在只使用 value 属性的时候可以省略不写
* value属性:用于指定 bean 的 id,如果不写,默认值就是当前类名,首字母小写
* 以下三个注解用法和 @Component 一样,为了区别不同层
* @Controller:一般用于表现层 ( Web层 )
* @Service:一般用于业务逻辑层 ( Service层 )
* @Repository:一般用于持久层 ( Dao层 )
*
* @Value 给属性赋值,赋值类型为基本数据类型以及String,可以在属性以及方法上使用
*/
//@Component("myCar")
@Component
@Data
public class Car {

@Value("20001")
private Integer cid;

//@Value("保时捷")
private String cname;

@Value("保时捷")
public void setCname(String cname) {
this.cname = cname;
}
}

User

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
package com.couture.pojo;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
* @Scope 表示当前对象默认单例( singleton ),可以设置为多例( prototype )
* 相当于 <bean scope = "" />
*/
@Component
@Scope(value = "singleton")
@Data
public class User {

@Value("1001")
private Integer id;

@Value("张三")
private String name;

@Value("123")
private String password;

/**
* @Autowired 表示自动装配,如果 spring容器中有该类型的对象,则自动注入到当前属性中
* @Qualifier@Autowired 一起用,指定要注入具体对象的名称,value 属性:指定注入 bean 的 id
* 如果只有一个对象,只使用 @Autowired,如果有多个同类型的对象,名称不能相同,使用 @Qualifier 选择具体的 Bean
*
* @Resource 是javaEE中的注解,name 属性:通过名称指定注入的 bean,相当于 @Autowired + @Qualifier
* @Resource 不常用,如果不生效,则需要导入
*/
@Autowired
@Qualifier("otherCar")
//@Resource(name = "otherCar")
private Car car;


/**
* @PostConstruct 相当于 <bean init-method = "" /> 表示初始化的方法( 构造器之后执行 )
*/
@PostConstruct
public void init(){
System.out.println("User 初始化");
}

/**
* @PreDestroy 相当于 <bean destroy-method = "" /> 表示销毁的方法 ( 对象销毁之前执行 )
*/
@PreDestroy
public void destroy(){
System.out.println("User 销毁");
}
}

applicationContext.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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"> <!-- bean definitions here -->

<!-- 扫描对应包下的注解 -->
<context:component-scan base-package="com.couture"></context:component-scan>

<!-- 初始化一个 Car 对象 -->
<bean name="otherCar" class="com.couture.pojo.Car">
<property name="cid" value="30001"></property>
<property name="cname" value="法拉利"></property>
</bean>

</beans>

bean.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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"> <!-- bean definitions here -->

<!-- 导入其他的xml文件-->
<import resource="applicationContext.xml"></import>

</beans>

测试

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
64
65
66
67
68
69
70
package com.couture.test;

import com.couture.pojo.Car;
import com.couture.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

/**
* 测试初始化 Car 对象相关注解的方法
*/
@Test
public void testCar() {

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

Car car = (Car) applicationContext.getBean("car");

System.out.println(car);
}

/**
* 测试初始化 User 对象相关注解的方法
*/
@Test
public void testUser() {

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

User user = (User) applicationContext.getBean("user");

System.out.println(user);
}

/**
* 测试作用范围关注解的方法
*/
@Test
public void testScope() {

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

User user1 = (User) applicationContext.getBean("user");
User user2 = (User) applicationContext.getBean("user");

System.out.println(user1 == user2);
}

/**
* 测试生命周期相关注解的方法
*/
@Test
public void testLife() {

//通过其他配置文件测试
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("bean.xml");

User user = (User) applicationContext.getBean("user");
System.out.println(user);

//关闭
applicationContext.close();
}

}

8.整合MyBatis 【重点】

8.1导入依赖
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?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">
<modelVersion>4.0.0</modelVersion>

<groupId>com.couture</groupId>
<artifactId>spring-05</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.9</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<!-- 连接数据库 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version>
</dependency>
<!-- mybatis 整合 spring 所需依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>

<!-- 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>


</dependencies>

</project>
8.2User类
1
2
3
4
5
6
7
8
9
10
11
12
package com.coutrue.pojo;

import lombok.Data;

@Data
public class User {

private Integer id;
private String name;
private String password;

}
8.3UserController
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.couture.controller;


import com.couture.pojo.User;
import com.couture.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.util.List;

/**
* web层 -> @Controller
*/
@Controller
public class UserController {

/**
* 注入 UserService
*/
@Autowired
private UserService userService;

/**
* 查询所有用户
* @return
*/
public List<User> findAll(){
return userService.findAll();
}

}
8.4UserService
1
2
3
4
5
6
7
8
9
10
11
12
13
01020304050607080910111213package com.couture.service;

import com.couture.pojo.User;

import java.util.List;

public interface UserService {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
8.5UserServiceImpl
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.qf.service.impl;

import com.coutrue.mapper.UserMapper;
import com.coutrue.pojo.User;
import com.coutrue.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* service层 -> @Service
*/
@Service
public class UserServiceImpl implements UserService {

/**
* 注入 UserMapper
*/
@Autowired
private UserMapper userMapper;

/**
* 查询所有用户
* @return
*/
@Override
public List<User> findAll() {
return userMapper.findAll();
}
}
8.6UserMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.coutrue.mapper;

import com.coutrue.pojo.User;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* dao层 -> @Repository
*/
@Repository
public interface UserMapper {

/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
8.7UserMapper.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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.coutrue.mapper.UserMapper">

<!-- orm映射 -->
<resultMap id="userMap" type="com.coutrue.pojo.User">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="password" column="password"></result>
</resultMap>

<!-- sql片段 -->
<sql id="baseSql">
select id, name, password from t_user
</sql>

<!-- 查询所有用户 -->
<select id="findAll" resultMap="userMap">
<include refid="baseSql"></include>
</select>

</mapper>
8.8db.properties
1
2
3
4
db.username = root
db.password = root
db.url = jdbc:mysql://localhost:3306/java?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.driver = com.mysql.cj.jdbc.Driver
8.9log4j.properties

properties

1
2
3
4
5
6
Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
8.10mybatis-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?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>

<!-- 配置日志 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>

</configuration>
8.11applicationContext.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
36
37
38
39
<?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"> <!-- bean definitions here -->

<!-- 导入外部配置文件 db.properties -->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>

<!-- 配置数据源对象 -->
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 导入 db.properties 中的值-->
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
<property name="url" value="${db.url}"></property>
<property name="driverClassName" value="${db.driver}"></property>
</bean>

<!-- 扫描对应包下的注解 -->
<context:component-scan base-package="com.coutrue"></context:component-scan>

<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 必选配置 -->
<property name="dataSource" ref="datasource"></property>
<!-- 非必选属性,根据自己需求去配置 -->
<!-- 导入 mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 导入 Mapper.xml 文件,classpath后面不能有空格 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>

<!-- 扫描 Mapper 接口,生成代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的具体位置 -->
<property name="basePackage" value="com.coutrue.mapper"></property>
</bean>
</beans>
8.12测试
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
package com.couture.test;

import com.couture.controller.UserController;
import com.couture.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class SpringTest {

/**
* 测试查询所有用户
*/
@Test
public void test_findAll(){

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

UserController userController = (UserController)applicationContext.getBean("userController");

List<User> userList = userController.findAll();

System.out.println(userList);
}
}

9.数据源配置类

用于替换applicationContext.xml中数据源的相关配置

<context:property-placeholder location=“classpath:db.properties”></context:property-placeholder>

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
package com.coture.config;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.couture.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.util.Properties;

/**
* @Configuration 表示服务器启动时加载当前类
* @PropertySource 加载对应的配置文件
*/
@Configuration
@PropertySource(value = "classpath:db.properties")
public class DataSourceConfig {

@Value("${db.username}")
private String username;

@Value("${db.password}")
private String password;

@Value("${db.url}")
private String url;

@Value("${db.driver}")
private String driverClassName;

/**
* 初始化一个 Bean 对象 -> DataSource
*
* @Bean 将方法的返回值作为 bean 对象,放到 spring 容器中
*/
@Bean("datasource")
public DataSource getDataSource() {
//设置数据源参数
Properties properties = new Properties();
properties.setProperty("username", username);
properties.setProperty("password", password);
properties.setProperty("url", url);
properties.setProperty("driverClassName", driverClassName);

DataSource dataSource = null;

try {
//创建数据源对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}

//返回
return dataSource;
}

}

10.分页

10.1导入依赖
1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
10.2第一种方式在mybatis-config.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
<?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>

<!-- 配置日志 -->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>

<!-- 分页插件 -->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 数据库方言,指定对应的数据库进行分页 -->
<property name="helperDialect" value="mysql"/>
<!-- 分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页 -->
<property name="reasonable" value="true"/>
<!-- 支持通过 Mapper 接口参数来传递分页参数 -->
<property name="supportMethodsArguments" value="true"/>
</plugin>
</plugins>

</configuration>
10.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
24
25
26
27
<!-- 配置sqlSessionFactory -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 必选配置 -->
<property name="dataSource" ref="datasource"></property>
<!-- 非必选属性,根据自己需求去配置 -->
<!-- 导入 mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 导入 Mapper.xml 文件,classpath后面不能有空格 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>

<!-- 配置分页 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<!--使用下面的方式配置参数,一行配置一个 -->
<value>
helperDialect = mysql
reasonable = true
supportMethodsArguments = true
</value>
</property>
</bean>
</array>
</property>

</bean>
10.4 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 测试分页查询
*/
@Test
public void test_findByPage(){

ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");

UserController userController = (UserController)applicationContext.getBean("userController");

PageInfo pageInfo = userController.findByPage(4, 2);

System.out.println(pageInfo.getList());
}

9.AOP

Aspect Oriented Programing 面向切面编程

springAOP:在程序运行期通过动态代理的方式向目标类(接口),织入增强的代码,为目标类(接口)中的方法添加额外的功能

采取是横向抽取机制,取代了我们传统的纵向继承方式重复性的代码

底层原理:代理模式

9.1装饰器模式

装饰器模式:对象本身增强

代理模式:代理对象(代理过程)增强

9.1.1Info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.couture.decorator;

/**
* 抽象构件
* 抽象类:可以有抽象方法,也可以有普通方法,但是一旦写了抽象方法,这个类一定为抽象类
*/
public abstract class Info {

/**
* 自我介绍
*/
public abstract void info();

}
9.1.2PersonInfo
1
2
3
4
5
6
7
8
9
10
11
12
package com.couture.decorator;

/**
* 具体构件
* 继承Info,实现方法
*/
public class PersonInfo extends Info{
@Override
public void info() {
System.out.println("自我介绍");
}
}
9.1.3Decorator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.couture.decorator;

/**
* 装饰器
* 抽象装饰
* 继承Info,实现方法,可以通过其子类扩展具体构件的功能
*/
public abstract class Decorator extends Info{

private Info info;

//传入被装饰对象
public Decorator(Info info){
this.info = info;
}

@Override
public void info() {
//调用自我介绍的方法
info.info();
}
}
9.1.4Singer
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
package com.couture.decorator;

/**
* 具体装饰
* 继承抽象装饰
*/
public class Singer extends Decorator{

//调用父类构造器
public Singer(Info info) {
super(info);
}

//自己的方法
public void singing(){
System.out.println("唱歌");
}

//重写方法,达到增强的目的
@Override
public void info() {
super.info();
singing();
}
}
9.1.5Dancer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.couture.decorator;

/**
* 具体装饰
* 继承抽象装饰
*/
public class Dancer extends Decorator{

public Dancer(Info info) {
super(info);
}

public void dancing(){
System.out.println("跳舞");
}

@Override
public void info() {
super.info();
dancing();
}
}
9.1.6Magic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.couture.decorator;

/**
* 具体装饰
* 继承抽象装饰
*/
public class Magic extends Decorator{

public Magic(Info info) {
super(info);
}

public void magic(){
System.out.println("变魔术");
}

@Override
public void info() {
super.info();
magic();
}
}
9.1.7测试
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
package com.couture.decorator;

public class Test {
public static void main(String[] args) {

//单独测试
// Info personInfo = new PersonInfo();
// personInfo.info();
// System.out.println("----------------------");
//
// Decorator singer = new Singer(personInfo);
// singer.info();
// System.out.println("----------------------");
//
// Dancer dancer = new Dancer(personInfo);
// dancer.info();
// System.out.println("----------------------");


//增强测试
Info personInfo = new PersonInfo();
Decorator singer = new Singer(personInfo);//第一次增强
Dancer dancer = new Dancer(singer);//第二次增强
Magic magic1 = new Magic(dancer);//第三次增强
Magic magic2 = new Magic(magic1);//第四次增强

magic2.info();
}
}

9.2代理模式

通过代理类对象,为目标类对象添加功能

分类:静态代理和动态代理

静态代理:需要实现接口中的方法,进行增强,代理的功能代码有冗余,维护性较差

动态代理:在不实现接口中所有方法,对接口中的指定的方法进行增强

9.2.1静态代理

Rent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.couture.proxy.demo1;

/**
* 接口
* 出租房子
*/
public interface Rent {

//出租房子
public void rent();

//其他的方法
//public void test();
}

Owner

1
2
3
4
5
6
7
8
9
10
11
package com.couture.proxy.demo1;

/**
* 房东
*/
public class Owner implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}

OwnerProxy

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
package com.couture.proxy.demo1;

/**
* 房东的代理对象
* 静态代理
*/
public class OwnerProxy implements Rent{

//房东对象
private Owner owner;

public OwnerProxy(Owner owner) {
this.owner = owner;
}

public void publish(){
System.out.println("发布租房信息");
}

public void seeHouse(){
System.out.println("带租户看房子");
}

@Override
public void rent() {
publish();
owner.rent();
seeHouse();
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.couture.proxy.demo1;

public class Test {
public static void main(String[] args) {

Owner owner = new Owner();
owner.rent();

System.out.println("----------------");

OwnerProxy ownerProxy = new OwnerProxy(owner);
ownerProxy.rent();

}
}
9.2.2动态代理

分为:JDK动态代理 和 CGLIB动态代理

JDK动态代理:基于接口的动态代理,被代理对象必须实现接口

CGLIB动态代理 :基于子类的动态代理,对目标对象进行继承代理

JDK动态代理

Rent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.couture.proxy.demo2;

/**
* 接口
* 出租房子
*/
public interface Rent {

//出租房子
public void rent();

//其他的方法
public void test();
}

Owner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.couture.proxy.demo2;

/**
* 房东
*/
public class Owner implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}

@Override
public void test() {
System.out.println("测试方法");
}
}

RentJdkProxy

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
package com.couture.proxy.demo2;

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

/**
* JDK动态代理
* 核心实现 InvocationHandler 接口,调用 invoke 方法
*/
public class RentJdkProxy implements InvocationHandler {

private Rent rent;

public void setRent(Rent rent) {
this.rent = rent;
}

//生成代理对象
public Rent getRent(){
return (Rent)Proxy.newProxyInstance(
rent.getClass().getClassLoader(), //类加载器
rent.getClass().getInterfaces(),//接口列表
this
);
}

//对接口中的方法进行增强(扩展),不需要实现接口中的所有方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//声明变量做为方法的返回值
Object result = null;

//判断方法名,对其进行增强
if("rent".equals(method.getName())){
publish();
result = method.invoke(rent,args);
seeHouse();
} else {
result = method.invoke(rent,args);
}

return result;
}


public void publish(){
System.out.println("发布租房信息");
}

public void seeHouse(){
System.out.println("带租户看房子");
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.couture.proxy.demo2;

public class Test {
public static void main(String[] args) {

Owner owner = new Owner();
owner.rent();

System.out.println("-------------------");

//创建RentJdkProxy对象
RentJdkProxy rentJdkProxy = new RentJdkProxy();
//给属性赋值
rentJdkProxy.setRent(owner);

//获取代理对象
Rent proxyRent = rentJdkProxy.getRent();
proxyRent.rent();

proxyRent.test();

}
}

9.3Spring中的AOP

面向切面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP),OOP的延伸

对目标对象中的多个不同方法进行不同程度的增强

AOP的术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):可以在运行期为类动态地添加一些方法或Field。
Target(目标对象):代理的目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。
Proxy(代理):一个类被AOP织入增强后,就产生一个代理类。
Aspect(切面):是切入点和通知(引介)的结合

9.4XML配置AOP

9.4.1导入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>

<!-- aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

</dependencies>
9.4.2UserService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.couture.service;

/**
* 目标对象 target
*/
public interface UserService {

//没有增强的方法,叫做连接点 JoinPoint
//被增强的方法,叫做切入点 PointCut
//增强的代码,叫做通知
public void add();

public void delete();

public void update();

public void query();
}
9.4.3UserServiceImpl
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.couture.service.impl;

import com.couture.service.UserService;

public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("add");
int i = 1/0;//算数异常
}

@Override
public void delete() {
System.out.println("delete");
}

@Override
public void update() {
System.out.println("update");
}

@Override
public void query() {
System.out.println("query");
}
}
9.4.4MyAdvice
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
package com.couture.advice;

import org.aspectj.lang.ProceedingJoinPoint;

/**
* 通知类:增强的代码
* 方法名以及功能可以是任意的
*/
public class MyAdvice {

public void before(){
System.out.println("前置通知,目标对象调用方法前执行");
}

public void after(){
System.out.println("后置通知(最终通知),目标对象调用方法后执行,无论目标对象方法是否发生异常都会执行");
}

public void after_returning(){
System.out.println("后置通知,目标对象调用方法后执行,目标对象方法发生异常则不执行");
}

public void after_throwing(){
System.out.println("异常通知,目标对象调用方法发生异常时执行");
}

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知,目标对象调用方法之前");
proceedingJoinPoint.proceed();
System.out.println("环绕通知,目标对象调用方法之后");
}

}
9.4.5applicationContext.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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->


<!-- 配置userService对象,对该对象中的方法进行增强 -->
<bean id="userService" class="com.coutrue.service.impl.UserServiceImpl"></bean>

<!-- 配置通知 -->
<bean id="myAdvice" class="com.coutrue.advice.MyAdvice"></bean>


<!-- 配置aop -->
<!-- 默认使用JDK动态代理,通过 proxy-target-class="true" 可以设置使用CGLIB动态代理 -->
<aop:config proxy-target-class="true">
<!-- 配置切点
expression:表达式,指定哪些方法是切入点,对那些进行增强
-->
<!-- <aop:pointcut id="pc" expression="execution(public void com.qf.service.impl.UserServiceImpl.add())"/>-->
<aop:pointcut id="pc" expression="execution(* com.coutrue.service.impl.*ServiceImpl.*(..))"/>

<!-- 配置切面,把通知配置到切点上 -->
<aop:aspect ref="myAdvice">
<!-- aop:before 前置通知,method 表示增强的代码的方法名 -->
<aop:before method="before" pointcut-ref="pc"></aop:before>

<!-- 后置通知(最终通知),目标对象调用方法后执行,无论目标对象方法是否发生异常都会执行 -->
<aop:after method="after" pointcut-ref="pc"></aop:after>

<!-- 后置通知,目标对象调用方法后执行,目标对象方法发生异常则不执行 -->
<aop:after-returning method="after_returning" pointcut-ref="pc"></aop:after-returning>

<!-- 异常通知,目标对象调用方法发生异常时执行 -->
<aop:after-throwing method="after_throwing" pointcut-ref="pc"></aop:after-throwing>

<!-- 环绕通知,目标方法调用之前和之后都会执行 -->
<aop:around method="around" pointcut-ref="pc"></aop:around>

</aop:aspect>

</aop:config>

</beans>
9.4.6测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.couture.test;

import com.couture.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

@Test
public void testAop(){

ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext("applicationContext.xml");

UserService userService = (UserService)applicationContext.getBean("userService");

userService.add();

//userService.delete();
}
}