Java之Spring AOP入门到精通IDEA版(一篇文章精通系列)
Posted 蓝盒子itbluebox
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之Spring AOP入门到精通IDEA版(一篇文章精通系列)相关的知识,希望对你有一定的参考价值。
一、设计模式-代理模式
代理模式:给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理
就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不
能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。
客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的
其他角色代为创建并传入。
为什么要使用代理模式呢?
第一,它有间接的特点,可以起到中介隔离作用。
就好比在租房的时候,房东可能不在本地,而短期内又不能赶回来,此时中介的出场,就作为房东的代理实现和我们签订承租合同。而我们和房东之间就没有耦合了。
第二,它有增强的功能。还以租房为例,我们首先考虑的是找一个靠谱的中介,由中介
给我们提供房源信息,并且告诉我们房屋的细节,合同的细节等等。
当然我们也可以自己去
找一些个人出租的房屋,但是在这之中,我们要了解租赁合同,房屋验收,租金监管等情
况,这无疑对我们是繁重的工作。
而有了中介作为我们的代理中间人,他把了解到的信息告诉我们,我们一样可以租到房子,而不必做那些繁重的工作。
二、AOP思想及实现原理
1、AOP思想
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2、实现原理
在上面的概念中描述出aop的实现原理是基于动态代理技术实现的。下面是针对动态代
理的一些介绍:
特点: 字节码随用随创建,随用随加载
分类: 基于接口的动态代理,基于子类的动态代理
作用: 不修改源码的基础上对方法增强
(1)基于接口的动态代理:
提供者是:JDK官方
使用要求:被代理类最少实现一个接口。
涉及的类:Proxy
创建代理对象的方法:newProxyInstance
方法的参数:
ClassLoader
:类加载器。用于加载代理对象的字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]
:字节码数组。用于给代理对象提供方法。和被代理对象具有相同的方法。
被代理类是一个普通类:被代理类对象.getClass().getInterfaces();
被代理类是一个接口:new Class[]{被代理了.class}
它也是固定写法InvocationHanlder:要增强的方法。此处是一个接口,我们需要提供它的实现类。通常写的是匿名内部类。增强的代码谁用谁写。
基于子类的动态代理
提供者是:第三方cglib包,在使用时需要先导包(maven工程导入坐标即可)
使用要求:被代理类不能是最终类,不能被final修饰
涉及的类:Enhancer
创建代理对象的方法:create
方法的参数:
Class:字节码。被代理对象的字节码。可以创建被代理对象的子类,还可以获取被代理对象的类加载器。
Callback:增强的代码。谁用谁写。通常都是写一个接口的实现类或者匿名内部类。
Callback中没有任何方法,所以我们一般使用它的子接口:MethodInterceptor
3、Spring中AOP的术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, 可以在运行期为类动态地添加一些方法或Field。
Target(目标对象):
代理的目标对象。
Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面):
是切入点和通知(引介)的结合。
三、Spring注解驱动AOP开发入门
1、写在最前
a.Spring的aop是基于ioc的。所以需要有spring的ioc基础。(本篇内容不对ioc进行讲解)
b.本章节我们只是对aop的使用做基本功能展示,目的是为了以此讲解aop中的注解和执行原理分析。
2、注解驱动入门案例介绍
需求:实现在执行service方法时输出执行日志。(除了业务层外,表现层和持久层也可以实现)
3、案例实现
- 工程搭建
- 引入依赖
<?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>cn.itbluebox</groupId>
<artifactId>spring-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--导入yaml文件解析器坐标-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
</project>
- 创建User实体类
package cn.itbluebox.pojo;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private String id;
private String username;
private String password;
private String email;
private Date birthday;
private String gender;
private String mobile;
private String nickname;
public User() {
}
public User(String id, String username, String password, String email, Date birthday, String gender, String mobile, String nickname) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.birthday = birthday;
this.gender = gender;
this.mobile = mobile;
this.nickname = nickname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\\'' +
", username='" + username + '\\'' +
", password='" + password + '\\'' +
", email='" + email + '\\'' +
", birthday=" + birthday +
", gender='" + gender + '\\'' +
", mobile='" + mobile + '\\'' +
", nickname='" + nickname + '\\'' +
'}';
}
}
- 业务层接口
package cn.itbluebox.service;
import cn.itbluebox.pojo.User;
public interface UserService {
/*
保存用户
*/
void save(User user);
}
- 业务层接口实现类
package cn.itbluebox.service.impl;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
public void save(User user) {
System.out.println("保存用户信息:"+user);
}
}
- 日志工具类
package cn.itbluebox.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogUtil {
/*
Pointcut通用切入点表达式
execution允许
*/
@Pointcut("execution(* cn.itbluebox.service.impl.*.*(..))")
private void pt1(){}
/*
前置通知
*/
@Before("pt1()")
public void beforeLog(){
System.out.println("执行切入点方法前记录日志");
}
/*
后置通知
*/
@AfterReturning("pt1()")
public void afterReturningLog(){
System.out.println("正常执行切入点方法后记录日志");
}
/*
异常通知
*/
@AfterReturning("pt1()")
public void afterThrowingLog(){
System.out.println("执行切入点方法产生异常后记录日志");
}
/*
最终通知
*/
@After("pt1()")
public void afterLog(){
System.out.println("无论切入点方法执行是否异常都记录日志");
}
/*
环绕通知
*/
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
//1.定义返回值
Object rtValue = null;
try{
//前置通知
System.out.println("执行切入点方法前记录日志");
//2.获取方法执行所需的参数
Object[] args = pjp.getArgs();
//3.执行切入点方法
rtValue = pjp.proceed(args);
//后置通知
System.out.println("正常执行切入点方法后记录日志");
}catch (Throwable t){
//异常通知
System.out.println("执行切入点方法产生异常后记录日志"+t);
}finally {
//最终通知
System.out.println("无论切入点方法执行是否有异常都记录日志");
}
return rtValue;
}
}
- 创建配置类:config.SpringConfiguration
package cn.itbluebox.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("cn.itbluebox")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
- 创建测试类:
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
//1、获取容器
AnnotationConfigApplicationContext
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2、获取Bean对象
UserService userService = ac.getBean("userService", UserService.class);
//3、准备数据
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("张三");
//4、执行方法
userService.save(user);
}
}
运行测试类
4、可以将切入点精确到方法
- 在接口当中创建update方法
public void update(User user) {
System.out.println("保存用户信息:"+user);
}
- 修改LogUtil
@Pointcut("execution(* cn.itbluebox.service.impl.UserServiceImpl.save(..))")
- 运行测试类
- 修改SpringAOPTest
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
//1、获取容器以上是关于Java之Spring AOP入门到精通IDEA版(一篇文章精通系列)的主要内容,如果未能解决你的问题,请参考以下文章
Java之Spring Boot入门到精通IDEA版SpringBoot原理分析,SpringBoot监控(一篇文章精通系列)下
Java之Spring入门到精通IDEA版IoC和DI注解开发(一篇文章精通系列)
Java之Spring入门到精通IDEA版IoC和DI注解开发(一篇文章精通系列)
Java之Spring入门到精通IDEA版Spring的IoC和DI(一篇文章精通系列)