Spring
1.介绍
官网:https://spring.io/
Spring 让每个人都可以更快、更轻松、更安全地编写 Java。Spring 对速度、简单性和生产力的关注使其成为世界上最受欢迎的Java 框架。
我们使用了 Spring 框架附带的许多工具,并获得了许多开箱即用的解决方案所带来的好处,并且不必担心编写大量额外的代码——这确实为我们节省了一些时间和能量。
2.特征
- 核心技术:依赖注入、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP。
- 测试:模拟对象、TestContext 框架、Spring MVC 测试、
WebTestClient
.- 数据访问:事务、DAO 支持、JDBC、ORM、编组 XML。
- Spring MVC和 Spring WebFlux Web 框架。
- 集成:远程处理、JMS、JCA、JMX、电子邮件、任务、调度、缓存。
- 语言:Kotlin、Groovy、动态语言。
3.核心技术
IOC:控制反转,将对象的创建权交给了Spring去管理
DI:依赖注入,把数据给创建好的对象中的属性进行赋值
AOP:面向切面编程,底层是代理模式
4.Bean的创建
org.springframework.beans
和org.springframework.context
包是 Spring Framework 的 IoC 容器的基础。该BeanFactory
接口提供了一种高级配置机制,能够管理任何类型的对象。ApplicationContext
是BeanFactory
的子接口。它补充说:
- 更容易与 Spring 的 AOP 功能集成
- 消息资源处理(用于国际化)
- 活动发布
- 应用层特定上下文,例如
WebApplicationContext
用于 Web 应用程序的上下文。
org.springframework.context.ApplicationContext
接口代表 Spring IoC 容器,负责实例化、配置和组装 bean。
4.1无参构造创建
导入依赖
<!-- 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实体类
package com.qf.pojo;
/**
* User实体类
*/
public class User {
private Integer id;
private String name;
private String password;
public User() {
System.out.println("无参构造");
}
}
编写applicationContext.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<bean id="user" class="com.qf.pojo.User">
</bean>
</beans>
编写测试类
package com.qf.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标签中的id属性值,获取bean对象
User user = (User)applicationContext.getBean("user");
System.out.println(user);
}
}
4.2工厂创建
创建工厂类
package com.qf.factory;
import com.qf.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
<?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.qf.pojo.User">-->
<!-- </bean>-->
<!-- 工厂中的实例方法创建Bean -->
<!-- <bean id = "userFactoryBean" class = "com.qf.factory.UserFactory"></bean>-->
<!-- <bean id = "user" factory-bean="userFactoryBean" factory-method="getUser"></bean>-->
<!-- 工厂中的静态方法创建Bean -->
<bean id="user" class="com.qf.factory.UserFactory" factory-method="getUserStatic"></bean>
</beans>
4.3简单工厂模式
Car
package com.qf.factorymode.simplefactory;
/**
* Car 接口
*/
public interface Car {
/**
* 提供 run 方法
*/
public void run();
}
package com.qf.factorymode.simplefactory;
/**
* Car 实现类
*/
public class BaoSJ implements Car {
@Override
public void run() {
System.out.println("保时捷在飞驰...");
}
}
package com.qf.factorymode.simplefactory;
/**
* Car 的实现类
*/
public class FaLL implements Car {
@Override
public void run() {
System.out.println("法拉利在飞驰...");
}
}
package com.qf.factorymode.simplefactory;
/**
* 简单工厂模式:代码集中,不符合 OCP 原则( open - close ):对代码的扩展是开放的,对代码的修改是关闭的
* 车工厂
*/
public class CarFactory {
//提供创建保时捷的方法
public static BaoSJ creatBaoSJ() {
try {
return (BaoSJ)Class.forName("com.qf.factorymode.simplefactory.BaoSJ").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//提供创建法拉利的方法
public static FaLL creatFaLL() {
try {
return (FaLL)Class.forName("com.qf.factorymode.simplefactory.FaLL").newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试
package com.qf.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
package com.qf.factorymode.abstractfactory;
/**
* Car 接口
*/
public interface Car {
/**
* 提供 run 方法
*/
public void run();
}
package com.qf.factorymode.abstractfactory;
/**
* Car 实现类
*/
public class BaoSJ implements Car {
@Override
public void run() {
System.out.println("保时捷在飞驰...");
}
}
package com.qf.factorymode.abstractfactory;
/**
* Car 的实现类
*/
public class FaLL implements Car {
@Override
public void run() {
System.out.println("法拉利在飞驰...");
}
}
CarFactory
package com.qf.factorymode.abstractfactory;
/**
* 抽象工厂模式:符合开闭原则
* 车工厂
*/
public interface CarFactory {
/**
* 创建车的方法
* @return
*/
public Car createCar();
}
package com.qf.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;
}
}
package com.qf.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;
}
}
测试
package com.qf.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应用的会话范围(全局会话)
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
scope:常用的是单例(singleton)和多例(prototype)
-->
<bean id = "user" class = "com.qf.pojo.User" scope="prototype">
</bean>
5.2单例模式-懒汉式
package com.qf.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单例模式-饿汉式
package com.qf.singletonmode;
/**
* 饿汉式:比较常用,容易产生垃圾( GC回收 )
* 优势:没有加锁,效率会提高
* 劣势:类加载时就进行初始化,消耗内存
*/
public class SingletonHungry {
//私有化构造器
private SingletonHungry() {}
//实例化
private static SingletonHungry singletonHungry = new SingletonHungry();
//方法
public static SingletonHungry getSingletonHungry() {
return singletonHungry;
}
}
5.4单例模式-双重校验锁
package com.qf.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生命周期
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
scope:常用的是单例(singleton)和多例(prototype)
init-method:创建对象后执行的初始化方法
destroy-method:对象销毁后执行(如果是多例模式下不执行)
-->
<bean id = "user" class = "com.qf.pojo.User"
scope="singleton" init-method="initUser" destroy-method="destroyUser">
</bean>
package com.qf.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 销毁方法");
}
}
测试
/**
* 测试生命周期相关方法
*/
@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配置文件
<?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.qf.pojo.Car">
<property name="cid" value="20001"></property>
<property name="cname" value="保时捷"></property>
</bean>
<!--
该id属性是标识单个 bean 定义的字符串。(不能重复)
该class属性定义 bean 的类型并使用完全限定的类名。
-->
<bean id = "user" class = "com.qf.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>
实体类
package com.qf.pojo;
import lombok.Data;
@Data
public class Car {
private Integer cid;
private String cname;
}
package com.qf.pojo;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private String password;
private Car car;
}
测试
package com.qf.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
package com.qf.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文件中添加内容
<!-- 复杂类型注入 -->
<bean id="collectionVo" class="com.qf.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>
测试
/**
* 测试复杂类型注入
*/
@Test
public void testCollectionVo() {
//加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
//创建Bean
CollectionVo collectionVo = (CollectionVo) applicationContext.getBean("collectionVo");
//输出
System.out.println(collectionVo);
}
7.3构造器注入( 不常用 )
创建Car
package com.qf.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
package com.qf.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文件
<?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.qf.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.qf.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.qf.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>
测试
package com.qf.test;
import com.qf.pojo.Car;
import com.qf.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
package com.qf.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
package com.qf.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配置文件
<?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.qf"></context:component-scan>
<!-- 初始化一个 Car 对象 -->
<bean name="otherCar" class="com.qf.pojo.Car">
<property name="cid" value="30001"></property>
<property name="cname" value="法拉利"></property>
</bean>
</beans>
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans
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>
测试
package com.qf.test;
import com.qf.pojo.Car;
import com.qf.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导入依赖
<?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.qf</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类
package com.qf.pojo;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private String password;
}
8.3UserController
package com.qf.controller;
import com.qf.pojo.User;
import com.qf.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
package com.qf.service;
import com.qf.pojo.User;
import java.util.List;
public interface UserService {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
8.5UserServiceImpl
package com.qf.service.impl;
import com.qf.mapper.UserMapper;
import com.qf.pojo.User;
import com.qf.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
package com.qf.mapper;
import com.qf.pojo.User;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* dao层 -> @Repository
*/
@Repository
public interface UserMapper {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
8.7UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mapper.UserMapper">
<!-- orm映射 -->
<resultMap id="userMap" type="com.qf.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
db.username = root
db.password = root
db.url = jdbc:mysql://localhost:3306/java2203?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.driver = com.mysql.cj.jdbc.Driver
8.9log4j.properties
# 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
<?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
<?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.qf"></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.qf.mapper"></property>
</bean>
</beans>
8.12测试
package com.qf.test;
import com.qf.controller.UserController;
import com.qf.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>
package com.qf.config;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.qf.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导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
10.2第一种方式在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>
<!-- 配置日志 -->
<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中配置
<!-- 配置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 测试
/**
* 测试分页查询
*/
@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
package com.qf.decorator;
/**
* 抽象构件
* 抽象类:可以有抽象方法,也可以有普通方法,但是一旦写了抽象方法,这个类一定为抽象类
*/
public abstract class Info {
/**
* 自我介绍
*/
public abstract void info();
}
9.1.2PersonInfo
package com.qf.decorator;
/**
* 具体构件
* 继承Info,实现方法
*/
public class PersonInfo extends Info{
@Override
public void info() {
System.out.println("自我介绍");
}
}
9.1.3Decorator
package com.qf.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
package com.qf.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
package com.qf.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
package com.qf.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测试
package com.qf.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
package com.qf.proxy.demo1;
/**
* 接口
* 出租房子
*/
public interface Rent {
//出租房子
public void rent();
//其他的方法
//public void test();
}
Owner
package com.qf.proxy.demo1;
/**
* 房东
*/
public class Owner implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
OwnerProxy
package com.qf.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();
}
}
测试
package com.qf.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
package com.qf.proxy.demo2;
/**
* 接口
* 出租房子
*/
public interface Rent {
//出租房子
public void rent();
//其他的方法
public void test();
}
Owner
package com.qf.proxy.demo2;
/**
* 房东
*/
public class Owner implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}
@Override
public void test() {
System.out.println("测试方法");
}
}
RentJdkProxy
package com.qf.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("带租户看房子");
}
}
测试
package com.qf.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导入依赖
<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
package com.qf.service;
/**
* 目标对象 target
*/
public interface UserService {
//没有增强的方法,叫做连接点 JoinPoint
//被增强的方法,叫做切入点 PointCut
//增强的代码,叫做通知
public void add();
public void delete();
public void update();
public void query();
}
9.4.3UserServiceImpl
package com.qf.service.impl;
import com.qf.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
package com.qf.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
<?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.qf.service.impl.UserServiceImpl"></bean>
<!-- 配置通知 -->
<bean id="myAdvice" class="com.qf.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.qf.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测试
package com.qf.test;
import com.qf.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();
}
}