文章目录
Spring之核心容器AOP
一 AOP概念
1 什么是AOP
(1)AOP是面向切面编程,利用AOP可以对业务逻辑的各个 部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)一句话描述:不通过修改源代码的方式,在主干概念里面添加新功能。
(3)案例说明
二 AOP底层原理
1 AOP使用动态代理
(1)第一种:有接口使用JDK动态代理
实现步骤:
① 定义一个接口及其实现类;
② 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
③ 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[]
interfaces,InvocationHandler h) 方法创建代理对象;
方法中的三个参数:
第一个:类加载器
第二个:增强方法所在的类,这个类所实现的接口,支持多个接口
第三个:实现这个接口的InvocationHandler,创建代理对象,写增强的部分
案例如下:
第一步:创建接口,定义方法,及其实现类
//接口
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
//实现类
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
第二步:创建自定义InvocationHandler
// 创建代理对象代码
class UserDaoProxy implements InvocationHandler {
//1 把创建的是谁的代理对象,把谁传递过来
// 有参数构造传递
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
// 增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法之前
System. out .println("方法之前执行...."+method.getName()+" :传递的参 数..."+ Arrays. toString (args));
// 被增强的方法执行
Object res = method.invoke(obj, args);
// 方法之后
System. out .println("方法之后执行...."+obj);
return res;
}
}
第三步:使用Proxy创建代理对象
public class JDKProxy {
public static void main(String[] args) {
// 创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao)Proxy. newProxyInstance (JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System. out .println("result:"+result);
}
}
(2)第二种:没有实现接口,使用CGLIB动态代理
实现步骤:
① 定义一个类;
② 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和JDK 动态代理中的 invoke 方法类似;
③ 通过 Enhancer 类的 create()创建代理类;
案例
第一步:定义类
public class Base {
/**
* 一个模拟的add方法
*/
public void add() {
System.out.println("add ------------");
}
}
第二步: 自定义 MethodInterceptor 并重写 intercept 方法
public class CglibProxy implements MethodInterceptor {
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
System.out.println("before-------------");
// 执行目标类add方法
proxy.invokeSuper(object, args);
// 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
System.out.println("after--------------");
return null;
}
}
第三步:通过 Enhancer 类的 create()创建代理类
//创建工厂类
public class Factory {
/**
* 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
*
* @param proxy
* @return
*/
public static Base getInstance(CglibProxy proxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Base.class);
//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(proxy);
// 此刻,base不是单纯的目标类,而是增强过的目标类
Base base = (Base) enhancer.create();
return base;
}
}
//测试类
public class Test {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
// base为生成的增强过的目标类
Base base = Factory.getInstance(proxy);
base.add();
}
}
三 AOP相关术语简介
1 连接点
连接点是类里面可以被增强的方法
2 切入点
切入点是实际被真正增强的方法
3 通知
(1)通知是实际增强的逻辑部分
(2)通知有许多种类型
①前置通知
② 后置通知
③ 环绕通知
④ 异常通知
⑤ 最终通知
4 切面
切面是一个把通知应用到切入点的过程动作。
四 AOP操作
1 准备工作
(1)spring框架一般都是基于AspectJ实现AOP操作
AspectJ不是Spring的组成部分,独立了AOP框架,一般把spring和AspectJ一起使用进行AOP操作
(2)基于AspectJ实现AOP操作有两种实现
① 基于XML配置文件实现
② 基于注解方式实现
(3)在项目工程种导入AOP相关依赖
(4) 切入点表达式
① 作用:知道对哪个类里的那个方法进行增强
② 语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )
案例:对包下所有类里的方法进行增强
execution(* com.atguigu.dao.. (…))
五 AspectJ注解操作AOP
1 创建类
public class User {
public void add() {
System. out .println("add.......");
}
}
2 创建增强类
在增强类里创建方法,让不同的方法代表不同通知类型
// 增强的类 public class UserProxy {
public void before() { // 前置通知
System. out .println("before......");
}
}
3 进行通知的配置
(1) 在spring配置文件中开启注解扫描
<? 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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<! -- 开启注解扫描 -- >
<context:component-scan basepackage="com.atguigu.spring5.aopanno"></context:component-scan>
(2) 使用注解创建User和UserProxy对象
(3) 在增强类上添加注解@Aspect
@Component
@Aspect
// 生成代理对象
public class UserProxy {
(4) 在spring配置文件中开启生成代理对象
<! -- 开启 Aspect 生成代理对象 -- > <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4 配置不同类型的通知
(1) 在增强类里,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
// 增强的类
@Component @Aspect
// 生成代理对象
public class UserProxy {
// 前置通知
//@Before 注解表示作为前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System. out .println("before.........");
}
// 后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System. out .println("afterReturning.........");
}
// 最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System. out .println("after.........");
}
// 异常通知
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing() {
System. out .println("afterThrowing.........");
}
// 环绕通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System. out .println("环绕之前.........");
// 被增强的方法执行
proceedingJoinPoint.proceed();
System. out .println("环绕之后.........");
}
}
5 相同切入点抽取
// 相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() { }
// 前置通知
//@Before 注解表示作为前置通知 @Before(value = "pointdemo()")
public void before() {
System. out .println("before.........");
}
6 设置增强类优先级
对于多个增强类对同一个方法进行增强,需要设置增强类优先级
在增强类上面添加注解@Order(数字类型值),值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy
7 完全使用注解开发
创建配置类,不需要创建XML配置文件
@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop { }
六 AspectJ配置文件操作AOP
1 创建两个类
创建增强类和被增强类,创建方法
public class Book{
public void buy() {
System. out .println("buy.......");
}
}
// 增强的类 public class BookProxy {
public void before() { // 前置通知
System. out .println("before......");
}
}
2 在spring配置文件创建两个类对象
<! -- 创建对象 -- >
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
3 在spring配置文件中配置切入点
<! -- 配置 aop 增强 -- >
<aop:config>
<! -- 切入点 -- >
<aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
<! -- 配置切面 -- >
<aop:aspect ref="bookProxy">
<! -- 增强作用在具体的方法上 -- >
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>