一、spring框架概述
1.spring是轻量级的开源javaEE框架
2.spring可以解决企业应用开发的复杂性
3.spring有两个核心部分:IOC和AOP
(1)IOC:控制反转,把创建对象过程交给spring进行管理
(2)AOP:面向切面,不修改源代码进行功能增强
4.spring特点:
(1)方便解耦,简化开发
(2)AOP编程支持
(3)方便程序测试
(4)方便和其他框架进行整合
(5)方便进行事务操作
(6)降低API开发难度
二、spring的核心组件
1.spring的核心组件
2.核心容器
核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
- spring-core 模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
- spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
- context 模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。
- spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。
依赖关系如图:
3.数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service【MQ】)
- JDBC 模块提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
- ORM 模块提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合,比如前面提及的事务管理。
- OXM 模块提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
- JMS 模块包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 spring-messaging 模块。
- 事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细)
4.web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
- Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
- Web-MVC 模块为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
- Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
- Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。
5.其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
- AOP 模块提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
- Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
- Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
三、SpringIOC的使用
3.1基本使用
3.1.1创建项目并导入SpringIoc相关的jar包
创建一个maven项目
在pom文件中,引入Spring的IoC核心jar包
<!--依赖-->
<dependencies>
<!--引入spring的context依赖,可以传递出aop beans core expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
</dependencies>
3.1.2创建user类
public class User {
private Integer id;
private String name;
private Integer age;
private String address;
private Date birth;
3.1.3创建Spring的核心配置文件spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
<bean id="userId" name="user" class="com.bjpowernode.domain.User" />
</beans>
3.1.4编写测试程序
public class Test {
public static void main(String[] args) {
//根据 spring的配置文件 创建 应用容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
System.out.println("马上获取bean");
//从容器中获取 对象
User user = (User) context.getBean("user");
System.out.println(user);
}
}
3.2 Bean标签属性介绍
3.2.1 id
是 bean的唯一标识 一个bean,其id 值只能有一个 。整个IOC 容器id 值不允许重复,使用名称作为key。
3.2.2 name
一个bean的名称,可以存在多个,多个之间使用逗号分隔。不论bean有没有定义name属性,默认id都会当做name。
3.2.3 class
bean的具体的类型,包名和类名组成。
3.2.4 scope
bean的作用域
prototype :非单例,每次获取都会创建一个新的bean对象。
singleton : 单例,多次获取永远同一个bean, 默认值。
request : 一次请求,基于web项目的bean的作用域。
session : 一次会话,基于web项目的bean的作用域。
3.2.5 lazy-init
延迟初始化,默认只要加载了配置文件。bean对象就会被初始化,lazy-init则是获取时才会初始化。只针对单例模式有效,非单例每次获取都会创建,没有延迟初始化的意义
3.2.6 depends-on
初始化时依赖的对象,当前对象初始化前需先初始化depends-on指定的对象
3.2.7 init-method
对象初始化后,调用的方法
3.2.8 destroy-method
对象销毁时,调用的方法
3.2.9 autowire
属性自动装配
byName 根据属性名称装配
byType 根据类型装配
3.2.10 autowire-candidate
是否允许作为自动装配的候选项
true 作为自动装配的候选项
false 不作为自动装配的候选项
3.2.11 primary
优先使用该bean,因为Spring需要支持使用类型查找对象,在一个大类型下,可能存在多个小类型。如果根据大类型装配属性时,不知道使用哪个具体的对象,则可以根据primary设置优先级。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- spring 核心配置文件 配置IOC容器中需要创建的bean -->
<!-- <bean id="userId" name="user" class="com.bjpowernode.domain.User" />-->
<!--
id : bean的唯一标识 整个IOC容器不能重复。
name : bean的key,多个name之间使用逗号,
class : 具体的bean的全路径
scope : bean的作用域
singleton 单例 默认
prototype 非单例
lazy-init="true" 获取时创建对象
depends-on="test" 默认自上而下创建 depends-on 会优先创建 depends-on 对应的bean
init-method : 对象创建后调用的方法
destroy-method :对象销毁时调用的方法, 容器调用close
autowire : 属性自动装配
byName 根据属性名装配
byType 根据属性类型装配
primary : 当存在多个同样的类型时, primary 为true 则优先使用该bean
-->
<bean id="userId2" name="user1,user2" class="com.bjpowernode.domain.User" scope="singleton" depends-on="test" init-method="init" destroy-method="destory" primary="true" />
<!-- test类 -->
<bean name="test" class="com.bjpowernode.domain.Test" />
<bean name="userService1" class="com.bjpowernode.service.impl.UserServiceImpl1" primary="true" />
<bean name="userService2" class="com.bjpowernode.service.impl.UserServiceImpl2" />
</beans>
`/*4
package com.bjpowernode.service.impl;
import com.bjpowernode.service.IUserService;
/**
* @Description: 接口实现类1
* @author: Mr.T
* @date 2020-09-26 14:33
*/
public class UserServiceImpl1 implements IUserService {
}
package com.bjpowernode.service.impl;
import com.bjpowernode.service.IUserService;
public class UserServiceImpl2 implements IUserService {
}
package com.bjpowernode.domain;
public class User {
private Integer id;
private String name;
public User(){
System.out.println("构造方法执行 user 对象进行创建");
}
public void sleep(){
System.out.println("早睡早起!!!");
}
public void init(){
System.out.println("对象初始化后调用的方法");
}
public void destory(){
System.out.println("对象销毁时调用");
}
}
package com.bjpowernode.test;
import com.bjpowernode.domain.User;
import com.bjpowernode.service.IUserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
//根据 spring的配置文件 创建 应用容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
System.out.println("马上获取bean");
//从容器中获取 对象
// 装备对象 : XX XXX1 XX2 xx3
User user = (User) context.getBean("user2");//
System.out.println("21:"+user);
System.out.println("22:"+context.getBean("user2"));
//获取接口的类型
System.out.println(context.getBean(IUserService.class));
context.close();//关闭容器 此时会调用销毁的方法
}
}
3.3 Bean对象创建的4种方式
3.3.1 构建方法的创建
使用构造方法创建bean对象,是spring默认的创建方式。
<!-- 使用构造方法创建对象 -->
<bean id="user" class="com.bjpowernode.domian.User" />
3.3.2 静态工厂创建
<!-- 使用静态工厂创建对象 -->
<!--
id : bean 的唯一标识
class : 工厂类
factory-method : 工厂方法
-->
<bean id="user2" class="com.bjpowernode.factory.UserStaticFactory" factory-method="getObj" />
package com.bjpowernode.factory;
import com.bjpowernode.domian.User;
public class UserStaticFactory {
/**
* 静态工厂中用于创建对象的方法
* @return
*/
public static User getObj(){
System.out.println("静态工厂中 创建对象的方法 执行了");
return new User();
}
}
3.3.3 非静态工厂创建
<!--
非静态工厂创建对象
在非静态工厂中,创建对象的方法是非静态方法。非静态方法的执行,首先需要该类对象
注意: 使用非静态工厂创建对象,首先需要创建工厂类对象
-->
<!-- 工厂类对象 -->
<bean id="userFactory" class="com.bjpowernode.factory.UserFactory" />
<!-- 使用非静态工厂创建对象 -->
<!--
factory-bean : 非静态工厂对象
factory-method : 创建对象的非静态方法
-->
<bean id="user3" factory-bean="userFactory" factory-method="getObj" />
package com.bjpowernode.factory;
import com.bjpowernode.domian.User;
public class UserFactory {
/**
* 工厂中用于创建对象的方法
* @return
*/
public User getObj(){
System.out.println("非静态工厂中创建对象的方法 执行了");
return new User();
}
}
3.3.4 注解创建
Spring为简化对象的创建方式,提供了注解。
组件注解
@Component
表示该类为一个被Spring管理的组件。但是,由于在开发中为了让代码的可读性更高。
Spring基于分层思想,将需要创建的组件分为以下几类:
@Controller
@Controller注解,标识该类是controller层的类。并且,注意在使用SpringMVC时,所有的Constroller,必须使用@Controller注解。
@Service
@Service注解,标识该类是业务层的类。
@Repository
@Repository注解,标识该类是操作数据层的类。
注意:
以上注解是Spring中定义的创建对象的注解,都可以创建对象,如果该类有明确的作用,有自己所属的层,则建议使用相应的注解,如果实在无法区分该类所属层,可以使用@Component注解。
注解的使用步骤
开启组件扫描
在spring的核心配置文件中,开启注解扫描,让Spring将被注解修饰的类,创建对相关。
<!--
开启组件扫描
-->
<context:component-scan base-package="com.bjpowernode.*" />
添加注解
package com.bjpowernode.domian;
import org.springframework.stereotype.Component;
@Component
public class Person {
public Person(){
System.out.println("Person的构造方法.........");
}
}
3.4 IOC 属性注入的3种方式
为对象属性设置值,就是属性注入。
构造方法属性注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 无参构造方法 -->
<bean class="com.bjpowernode.domain.User" />
<!--
有参数的构造方法
此时是2个参数的构造方法
index : 参数下标 从 0开始
value : 属性的值
-->
<bean id="user2" class="com.bjpowernode.domain.User" >
<constructor-arg index="0" value="1001" />
<constructor-arg index="1" value="韩梅梅" />
</bean>
<!-- 有参数的构造方法
使用index下标查找 属性 存在问题 都只有一个参数 则默认使用后面的构造方法
可以使用 type 指定参数的类型
更推荐 使用name属性 : name表示构造器中参数的名称
-->
<bean id="user3" class="com.bjpowernode.domain.User">
<constructor-arg index="0" value="1001" type="java.lang.Integer" />
</bean>
<bean id="user4" class="com.bjpowernode.domain.User">
<constructor-arg name="name" value="韩梅梅" />
</bean>
</beans>
package com.bjpowernode.domain;
public class User {
/**
* id 属性
*/
private Integer id;
/**
* name 属性
*/
private String name;
public User(){
System.out.println("无参数构造方法");
}
public User(Integer id) {
System.out.println("id参数方法");
this.id = id;
}
public User(String name) {
System.out.println("name参数构造方法");
this.name = name;
}
public User(Integer id, String name) {
System.out.println("id,name参数构造方法");
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
set方法属性注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
使用set方法进行属性注入
property 表示属性
name : 表示属性对应的set方法名,去掉set前缀,首字母小写。并不是真正的属性名
-->
<bean id="student1" class="com.bjpowernode.domain.Student">
<property name="studentId" value="1001" />
</bean>
</beans>
注解属性注入
在spring中,为了简化属性的注入,Spring提供注解:@Autowired,Spring会自动从IOC容器中,为这个属性查找相应类型的值,进行注入。
开启包的注解扫描
<context:component-scan base-package="com.*" />
使用注解
package com.bjpowernode.domain;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
public class Student {
public Integer id;
public String name;
@Autowired //使用注解自动注入
//User 对象
public User user;
public void setStudentId(Integer id) {
System.out.println("set方法被调用了............");
this.id = id;
}
/* public void setUser(User user) {
this.user = user;
}*/
}
注意:
在使用自动注入时,可以在bean标签上,配置autowire,但是此时必须有该属性的set方法,@Autowired注解,是不需要set方法的。
如果是在xml中注入对象,值使用ref属性。value属性,只支持boolean,数字,字符串等。
常见类型的属性注入
在Spring中,提供了丰富的标签,进行各种属性的注入。常见的类型:
数字、字符串、boolean、数组、set、list、map、properties。
package com.bjpowernode.domain;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Person {
private Integer id;
private String name;
private boolean sex;// true 男 false 女
private String[] likes;//爱好
private Set<String> girlFriends; //女朋友
private List<Dream> dreams;//梦想
private Map<String,String> house; //房子
private Properties properties; //配置文件属性
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public String[] getLikes() {
return likes;
}
public void setLikes(String[] likes) {
this.likes = likes;
}
public Set<String> getGirlFriends() {
return girlFriends;
}
public void setGirlFriends(Set<String> girlFriends) {
this.girlFriends = girlFriends;
}
public List<Dream> getDreams() {
return dreams;
}
public void setDreams(List<Dream> dreams) {
this.dreams = dreams;
}
public Map<String, String> getHouse() {
return house;
}
public void setHouse(Map<String, String> house) {
this.house = house;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}package com.bjpowernode.domain;
public class Dream {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 简单的属性注入 : 基本数据类型 + string -->
<bean id="person1" class="com.bjpowernode.domain.Person" >
<!-- Integer 类型的属性注入 -->
<property name="id" value="1001" />
<!-- String 类型的属性注入 -->
<property name="name" value="佚名" />
<!-- boolean 属性注入 -->
<property name="sex" value="true" />
</bean>
<!-- 数组类型 -->
<bean id="person2" class="com.bjpowernode.domain.Person" >
<property name="likes" >
<array>
<value>足球</value>
<value>篮球</value>
<value>羽毛球</value>
</array>
</property>
</bean>
<!-- set 类型注入 -->
<bean id="person3" class="com.bjpowernode.domain.Person" >
<property name="girlFriends" >
<set>
<value>韩梅梅</value>
<value>Lucy</value>
<value>Lucy</value>
<value>Rose</value>
</set>
</property>
</bean>
<bean id="dream1" class="com.bjpowernode.domain.Dream">
<property name="title" value="数钱数到手抽筋" />
</bean>
<!-- list 类型 -->
<bean id="person4" class="com.bjpowernode.domain.Person" >
<property name="dreams" >
<list>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="解放全人类" />
</bean>
<bean class="com.bjpowernode.domain.Dream">
<property name="title" value="世界和平" />
</bean>
<ref bean="dream1"/>
</list>
</property>
</bean>
<!-- map结构 -->
<bean id="person5" class="com.bjpowernode.domain.Person" >
<property name="house" >
<map>
<entry key="wh" value="江滩" />
<entry key="bj" value="后海" />
<entry key="hz" value="西湖" />
</map>
</property>
</bean>
<!-- properties -->
<bean id="person6" class="com.bjpowernode.domain.Person" >
<property name="properties" >
<props>
<prop key="driver">驱动</prop>
<prop key="url">url</prop>
</props>
</property>
</bean>
</beans>
package com.bjpowernode.test;
import com.bjpowernode.domain.Dream;
import com.bjpowernode.domain.Person;
import com.bjpowernode.domain.Student;
import com.bjpowernode.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
Person person1 = (Person) context.getBean("person1");
System.out.println(person1.getId() +" "+ person1.getName() + " "+ person1.getSex() );
Person person2 = (Person) context.getBean("person2");
String[] likes = person2.getLikes();
System.out.println(Arrays.asList(likes));
Person person3= (Person) context.getBean("person3");
System.out.println(person3.getGirlFriends());
Person person4 = (Person) context.getBean("person4");
List<Dream> dreams = person4.getDreams();
System.out.println(dreams.get(0).getTitle());
System.out.println(dreams.get(1).getTitle());
System.out.println(dreams.get(2).getTitle());
Person person5 = (Person) context.getBean("person5");
System.out.println(person5.getHouse());
Person person6 = (Person) context.getBean("person6");
Properties properties = person6.getProperties();
System.out.println(properties.getProperty("driver"));
System.out.println(properties.getProperty("url"));
}
}
四、AOP
4.1 AOP简介
代理
静态代理
静态代理,每个被代理类都需要创建对应的代理类。随着程序的扩展,代理类也会增多,臃肿,维护量变多,为了解决这个问题,Java中,提供了动态代理技术,开发者不需要自己定义代理类,代理类由JDK动态的创建,开发只需要指定被代理的类即可。
4.2 动态代理
4.2.1JDK动态代理
proxy:
该类提供了方法创建代理类和代理类的对象的方法
创建一个代理类并返回代理类对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader : 类加载器,指定类加载器,是为了精确的定位类
interfaces : 接口Class类,使用JDK的反射,必须要有接口
h :InvocationHandler ,代理的处理器,每个代理类都有一个关联的处理器
InvocationHandler:
是每个代理类对应的处理器
Object 方法调用的返回值,可以作为被代理的方法调用的返回值
proxy : 代理类对象
method : 目标类中被代理的方法
args : 目标类中被代理的方法的运行参数
Object invoke(Object proxy,Method method,Object[] args)
JDK动态代理的不足
在JDK中使用动态代理,必须有类的接口。因为生成的代理需要实现这个接口,这样我们生成的代理类对象,才能转化为代理目标的接口对象,然后根据接口中的方法,调用处理器中invoke方法。
4.2.Cglib动态代理
为了弥补JDK动态代理的不足,第三方组织封装一套工具包,cglib的工具包,这套包不基于接口,基于父子继承,通过重写的形式扩展方法,但是这个子类工具自动生成的。
早期,Cglib动态代理,性能相于JDK的动态代理高一些。JDK进行一些列优化,目前Spring默认使用的动态代理JDK,也支持Cglib。
Cglib动态代理的使用
MethodInterceptor:
cglib中,提供的对方法执行拦截的接口。其中intercept是对具体方法进行拦截处理的方法。
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy)
Object : 方法执行返回的结果
obj :增强类的对象
method :目标方法
proxy :用于回调的方法的对象
动态代理的不足
不论是JDK的动态代理,还是第三方cglib动态代理,都需要开发者编写代码处理程序。程序结构基本上大同小异,重复造轮子。基于这样的情况,在Spring中,提供了2种方式:xml配置形式和注解形式,使用动态代理。这种模式就是Spring Aop技术。其底层依然是动态代理。
4.3 SpringAoP的配置
4.3.1 AoP的相关配置
连接点(JoinPoint):所谓连接点是指那些被拦截的点,而spring中这些点就是指方法,因为spring只支持方法类型的连接点。
切入点(PointCut):所谓切入点就是指我们要对那些JoinPoint进行拦截的定义,指的是具体的拦截的位置
增强/通知(Advice) : 增强就是对具体的连接点进行扩展的功能。由于一般对方法进行增强,分为在方法前执行或者方法后,或者发生异常执行等等,所以增强被分为:前置增强(前置通知)、后置增强(后置通知)、环绕通知(环绕增强)、异常增强(异常通知)
引介(Introduction):引介是一种特殊的Advice,在不修改代码的前提下,引介可以在运行期为类动态的添加一些方法或Field.
目标(Target) :被代理的类(需要增强类)
织入(Weaving) :把Advice应用到Target的过程
代理(Proxy):使用AOP配置后产生的代理类
切面(Aspect):切点和增强整合形成了切面
4.3.2 Spring自身AOP具体配置
①引入aop相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>02-spring-aop01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.2.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
②定义增强类
前置增强-MethodBeforeAdvice
package com.bjpowernode.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @Description: 后置增强接口
* @author: Mr.T
* @date 2020-09-27 15:55
*/
public class MyAfterAdvice implements AfterReturningAdvice {
/**
*
* @param returnValue 被增强的方法运行后返回的数据
* @param method 被增强的方法
* @param args 方法运行的参数
* @param target 目标类对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置增强方法");
}
}
环绕增强-MethodInterceptor
package com.bjpowernode.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* @Description: 环绕增强
* @author: Mr.T
* @date 2020-09-27 16:04
*/
public class MyAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("前置增强");
Object rs = invocation.proceed();//调用 目标方法
System.out.println("后置增强");
return rs;
}
}
异常增强-ThrowsAdvice
异常增强中的增强方法必须叫:afterThrowing,并且必须定义参数接收发生的异常信息。
import org.springframework.aop.ThrowsAdvice;
/**
* @Description: 异常增强类
* @author: Mr.T
* @date 2020-09-27 16:12
*/
public class MyExceptionAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("异常增强的方法!!!!!!");
}
}
③目标类
public interface ITargetClass {
/**
* 待增强的目标方法
*/
void targetMethod();
/**
* 待增强的方法
* @param msg
* @return
*/
String afterTargetMethod(String msg);
/**
* 环绕增强的方法
*/
void aroundTargetMethod();
/**
* 执行会发生异常
*/
void runException();
}
package com.bjpowernode.impl;
import com.bjpowernode.ITargetClass;
/**
* @Description: 目标类
* @author: Mr.T
* @date 2020-09-27 15:38
*/
public class TargetClassImpl implements ITargetClass {
@Override
public void targetMethod() {
System.out.println("待增强的目标方法");
}
@Override
public String afterTargetMethod(String msg) {
System.out.println("待增强的目标方法 --- afterTargetMethod");
return "被增强的方法的返回值";
}
public void aroundTargetMethod() {
System.out.println("待增强的目标方法 --- aroundTargetMethod");
}
/**
* 发生异常
*/
public void runException() {
System.out.println("待增强的目标方法 --- runException");
int m = 0;
int n = 100/m;
}
}
④ AOP配置
<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 定义目标类对象 -->
<bean id="targetClass" class="com.bjpowernode.impl.TargetClassImpl" />
<!-- 定义增强类对象 前置增强类 -->
<bean id="myBeforeAdvice" class="com.bjpowernode.advice.MyBeforeAdvice" />
<!-- 定义增强类对象 后置增强 -->
<bean id="myAfterAdvice" class="com.bjpowernode.advice.MyAfterAdvice" />
<!-- 定义增强类 环绕增强 -->
<bean id="myAroundAdvice" class="com.bjpowernode.advice.MyAroundAdvice" />
<!-- 定义增强类 异常增强类 -->
<bean id="myExceptionAdvice" class="com.bjpowernode.advice.MyExceptionAdvice" />
<!-- 进行织入 -->
<aop:config>
<!--
id : 连接点的唯一标识
expression : 连接点的表达式
execution(* 包名.类名.方法名(..))
* 指任意字符
.. 表示参数可以是任意个
-->
<aop:pointcut id="beforePoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.targetMethod(..))"/>
<!-- 后置增强的切点 -->
<aop:pointcut id="afterPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.afterTargetMethod(..))"/>
<!-- 环绕增强的切点 -->
<aop:pointcut id="aroundPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.aroundTargetMethod(..))"/>
<!-- 异常增强的切点 -->
<aop:pointcut id="exceptionPoint" expression="execution(* com.bjpowernode.impl.TargetClassImpl.runException(..))"/>
<!--
织入
将增强和连接点 结合
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="beforePoint" />
<!-- 织入
后置增强的织入
-->
<aop:advisor advice-ref="myAfterAdvice" pointcut-ref="afterPoint" />
<!--
织入
环绕增强的织入
-->
<aop:advisor advice-ref="myAroundAdvice" pointcut-ref="aroundPoint" />
<!--
织入
异常增强的织入
-->
<aop:advisor advice-ref="myExceptionAdvice" pointcut-ref="exceptionPoint" />
</aop:config>
</beans>
⑤测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
ITargetClass targetClass = context.getBean(ITargetClass.class);
targetClass.runException();
}
}