基于SpringBoot实现AOP+jdk/CGlib动态代理详解
动态代理是一种设计模式。在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理。
JDK动态代理
首先定义一个人的接口:
public interface Person {
void study();
}
然后接上一个Student class
public class Student implements Person{
@Override
public void study() {
System.out.println("学生要学习");
}
}
然后我们创建一个动态代理类,需要实现InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class AnimalInvocationHandler implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
Object result = null;
System.out.println("----调用前处理");
result = method.invoke(target,args);
System.out.println("----调用后处理");
return result;
}
}
然后给一个main方法。
public class Test {
public static void main(String[] args) {
Student dog = new Student();
AnimalInvocationHandler ani = new AnimalInvocationHandler();
Person proxy = (Person)ani.bind(dog);
proxy.study();
}
}
运行结果如下。
想要在student对象前后加上额外的逻辑,可以不直接修改study方法。
这就是AOP实现的基本原理,只是Spring不需要开发人员自己维护。
但是这么实现有个缺点,那就是必须实现接口。烦死了。所以我们要用CGLIB了。
CGLIB动态代理
首先把。这玩意是个开源包。
给个下载地址:
https://repo1.maven.org/maven2/cglib/cglib/3.3.0/cglib-3.3.0.jar
https://repo1.maven.org/maven2/org/ow2/asm/asm/7.0/asm-7.0.jar
下载之后添加到eclipse里面。
首先是Teacher类
public class Teacher {
public void play(){
System.out.println("老师改作业");
}
}
然后是这个,需要重写MethodInterceptor
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class TeacherMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o,Method method,Object[] objects,MethodProxy methodProxy) throws Throwable{
System.out.println("调用前。。。");
Object obj = methodProxy.invokeSuper(o,objects);
System.out.println("调用后。。。");
return obj;
}
}
main方法如下所示
import net.sf.cglib.proxy.Enhancer;
public class CglibDemo {
public static void main(String[] args) {
Enhancer en = new Enhancer();
en.setSuperclass(Teacher.class);
en.setCallback(new TeacherMethodInterceptor());
Teacher t = (Teacher)en.create();
t.play();
}
}
运行结果如下:
这就实现了横向编程。
AOP
面向切面编程是面向对象编程的一种补充。
以Java为例,提供了封装,继承,多态等概念,实现了面向对象编程。但是假如我们要实现以下场景。
给每个类设置权限拦截器。
如果不用AOP思想,我们都能疯掉。因为会有大量代码重用重写。但是AOP的出现提供“横向”的逻辑,将与多个对象有关的公共模块分装成一个可重用模块,并且将这个模块整合成Aspect,即切面。
AOP的一些概念,整理成表如下:
名称 | 概念 |
---|---|
横切关注点 | 一个横切需求(例如日志) |
切面 | 一个横切关注点可能有多个对象 |
连接点 | 一个方法的执行 |
切入点 | AspectJ的切入点语法 |
通知 | 拦截后的动作 |
目标对象 | 业务中需要增强的对象 |
织入 | 将切面作用到对象 |
引入 | 不用定义接口就能使用其中的方法 |
Spring的AOP实现
由于Spring framework 的依赖过多,具体哪个jar包缺了啥报啥错啥版本能把我弄吐血。
为了头发,我这里采用SpringBoot来实现AOP
首先打开InteliJ
new Project 完之后一直点就行。
啥都不用勾选。
然后我们会发现
启动如果没报错,那就完事。
报错了去搜搜怎么搭建Spring-boot。都是一键生成的。
下面开始敲代码:注意!一个东西都不能落下!!
首先我们修改一下pom文件
我的pom文件如下所示:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
完整路径如下所示:
首先是Fruit类
package com.example.demo1;
public interface Fruit {
void eat();
}
然后是Apple类
package com.example.demo1;
import org.springframework.stereotype.Component;
@Component
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
Orange类
package com.example.demo1;
import org.springframework.stereotype.Component;
@Component
public class Orange implements Fruit {
@Override
public void eat() {
System.out.println("吃桔子");
}
}
然后是FruitAnnotationHandler 类
@execution的含义是匹配该包下任意类的任意方法名的任意入参的任意方法返回值。
@Aspect用来声明这是切面,注解“@Before”用来表明前置通知,“@After用来表示后置通知”
package com.example.demo1;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class FruitAnnotationHandler {
/**
* 定义切点
*/
@Pointcut("execution(* com.example.demo1.*.*(..))")
public void eatFruit(){
}
/**
* 前置通知
*/
@Before("eatFruit()")
public void startEatFruit(){
System.out.println("要开始吃了");
}
/**
* 后置通知
*/
@After("eatFruit()")
public void endEatFruit(){
System.out.println("吃完了");
}
}
最后是Application类
package com.example.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(Demo1Application.class, args);
Fruit apple = app.getBean(Apple.class);
Fruit orange = app.getBean(Orange.class);
apple.eat();
orange.eat();
}
}
然后运行~
运行成功完美!
其实SpringBoot默认的AOP实现就是使用的CGLib代理。
我们并不用定义哪个Fruit接口。
但是你如果脾气倔,非要用jdk代理的话。
把这个加上就OK了。
如果你没定义接口的话,下场就是这样。
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo1.Apple' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:337)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1123)
at com.example.demo1.Demo1Application.main(Demo1Application.java:11)
至此我们就完成了AOP的入门
基于SpringBoot实现AOP+jdk/CGlib动态代理详解的更多相关文章
- JDK、CGlib动态代理详解
Java动态代理之JDK实现和CGlib实现(简单易懂) 一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...
- JDK动态代理、CGLIB动态代理详解
Spring的AOP其就是通过动态代理的机制实现的,所以理解动态代理尤其重要. 动态代理比静态代理的好处: 1.一个动态代理类可以实现多个业务接口.静态代理的一个代理类只能对一个业务接口的实现类进行包 ...
- 【spring基础】AOP概念与动态代理详解
一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
- 代理模式详解:静态代理+JDK/CGLIB 动态代理实战
1. 代理模式 代理模式是一种比较好的理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...
- SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架
1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...
- spring---aop(4)---Spring AOP的CGLIB动态代理
写在前面 前面介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程. CGLib全称为Code Generation Library,是一个强 ...
- JAVA动态代理详解
1.什么是代理 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 2.什么是动态代理 在程 ...
- Spring AOP关于cglib动态代理
一: Spring AOP的默认代理方式是jdk动态代理,还有另外一种代理方式是cglib代理,简单说前者基于接口,后者基于继承,基本思路是将被代理对象的类作为父类,然后创建子类来进行方法的调用,调用 ...
- Spring @Trasactionl 失效, JDK,CGLIB动态代理
@Transaction: http://blog.csdn.net/bao19901210/article/details/41724355 Spring上下文: http://blog.csd ...
随机推荐
- C#设计模式开启闯关之路
前言背景 这是一条望不到尽头的编程之路,自踏入编程之路开始.就面临着各式各样的挑战,而我们也需要不断的挑战自己.不断学习充实自己.打好坚实的基础.以使我们可以走的更远.刚踏入编程的时候.根据需求编程, ...
- SpringIoC和SpringMVC的快速入门
更多内容,欢迎关注微信公众号:全菜工程师小辉~ Spring的优势? 降低了组件之间的耦合性 ,实现了软件各层之间的解耦 可以使用容易提供的众多服务,如事务管理,消息服务等 容器提供单例模式支持 容器 ...
- Leetcode之回溯法专题-212. 单词搜索 II(Word Search II)
Leetcode之回溯法专题-212. 单词搜索 II(Word Search II) 给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词. 单 ...
- QFramework 使用指南 2020(三):脚本生成(1)基本使用
在上一篇,我们对 QFramework 的两个主要版本提供了介绍,并且写下了第一个 QFramework 脚本. 在这一篇,我们学习 QFramework 中几乎每个项目都要用到并且从中受益的功能:自 ...
- 新手学习FFmpeg - 调用API完成录屏并进行H.264编码
Screen Record H.264 目前在网络传输视频/音频流都一般会采用H.264进行编码,所以尝试调用FFMPEG API完成Mac录屏功能,同时编码为H.264格式. 在上一篇文章中,通过调 ...
- Git学习笔记-相关命令记录
内容来自:https://www.liaoxuefeng.com/wiki/896043488029600/896067074338496 1.Linux安装Git 首先,你可以试着输入git,看看系 ...
- POJ - 3436 ACM Computer Factory 网络流
POJ-3436:http://poj.org/problem?id=3436 题意 组配计算机,每个机器的能力为x,只能处理一定条件的计算机,能输出特定的计算机配置.进去的要求有1,进来的计算机这个 ...
- LuoGuP1516 青蛙的约会 + 同余方程 拓展欧几里得
题意:有两只青蛙,在一个圆上顺时针跳,问最少的相遇时间. 这个是同余方程的思路.可列出方程:(m-n)* X% L = y-x(mod L) 简化为 a * x = b (mod L) (1 ...
- 线段树离散化 unique + 二分查找 模板 (转载)
离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率. 通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小.例如: 原数据:1,999,100000,15:处理 ...
- codeforces 793 D. Presents in Bankopolis(记忆化搜索)
题目链接:http://codeforces.com/contest/793/problem/D 题意:给出n个点m条边选择k个点,要求k个点是联通的而且不成环,而且选的边不能包含选过的边不能包含以前 ...