cglib动态代理_基于SpringBoot实现AOP+jdk/CGlib动态代理详解

动态代理是一种设计模式。在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理。

JDK动态代理#

首先定义一个人的接口:

Copypublic interface Person { void study();}

然后接上一个Student class

Copypublic class Student implements Person{ @Override public void study() { System.out.println("学生要学习"); }}

然后我们创建一个动态代理类,需要实现InvocationHandler接口

Copyimport 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方法。

Copypublic class Test { public static void main(String[] args) { Student dog = new Student(); AnimalInvocationHandler ani = new AnimalInvocationHandler(); Person proxy = (Person)ani.bind(dog); proxy.study(); }}

运行结果如下。

371fe972ce60d77f97d0111315502f20.png

想要在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里面。

657de6850fc6a23fe26642d96b11bd2d.png

首先是Teacher类

Copypublic class Teacher { public void play(){ System.out.println("老师改作业"); }}

然后是这个,需要重写MethodInterceptor

Copyimport 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方法如下所示

Copyimport 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(); }}

运行结果如下:

c40a8baf0d6c40297ab6322afd439e2c.png

这就实现了横向编程。

AOP#

面向切面编程是面向对象编程的一种补充。

以Java为例,提供了封装,继承,多态等概念,实现了面向对象编程。但是假如我们要实现以下场景。

给每个类设置权限拦截器。

如果不用AOP思想,我们都能疯掉。因为会有大量代码重用重写。但是AOP的出现提供“横向”的逻辑,将与多个对象有关的公共模块分装成一个可重用模块,并且将这个模块整合成Aspect,即切面。

AOP的一些概念,整理成表如下:

名称概念横切关注点一个横切需求(例如日志)切面一个横切关注点可能有多个对象连接点一个方法的执行切入点AspectJ的切入点语法通知拦截后的动作目标对象业务中需要增强的对象织入将切面作用到对象引入不用定义接口就能使用其中的方法

Spring的AOP实现#

由于Spring framework 的依赖过多,具体哪个jar包缺了啥报啥错啥版本能把我弄吐血。

为了头发,我这里采用SpringBoot来实现AOP

首先打开InteliJ

91ae712b91606734da22bef71e49063c.png

new Project 完之后一直点就行。

啥都不用勾选。

然后我们会发现

55babc9af37f9a3b04a74c8f02f9ef1b.png

启动如果没报错,那就完事。

报错了去搜搜怎么搭建Spring-boot。都是一键生成的。

下面开始敲代码:注意!一个东西都不能落下!!

首先我们修改一下pom文件

我的pom文件如下所示:

Copy<?xml version="1.0" encoding="UTF-8"?>4.0.0org.springframework.boot spring-boot-starter-parent 2.1.8.RELEASEcom.example demo1 0.0.1-SNAPSHOTdemo1Demo project for Spring Boot1.8org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-test testorg.springframework.boot spring-boot-maven-plugin 

完整路径如下所示:

d7f290faa7486527018e695def5f334d.png

首先是Fruit类

Copypackage com.example.demo1;public interface Fruit { void eat();}

然后是Apple类

Copypackage com.example.demo1;import org.springframework.stereotype.Component;@Componentpublic class Apple implements Fruit { @Override public void eat() { System.out.println("吃苹果"); }}

Orange类

Copypackage com.example.demo1;import org.springframework.stereotype.Component;@Componentpublic class Orange implements Fruit { @Override public void eat() { System.out.println("吃桔子"); }}

然后是FruitAnnotationHandler 类

@execution的含义是匹配该包下任意类的任意方法名的任意入参的任意方法返回值。

@Aspect用来声明这是切面,注解“@Before”用来表明前置通知,“@After用来表示后置通知”

Copypackage 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@Aspectpublic 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类

Copypackage com.example.demo1;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationContext;@SpringBootApplicationpublic 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(); }}

然后运行~

1b53f6e6780fcd750b0b643b682fdef8.png

运行成功完美!

其实SpringBoot默认的AOP实现就是使用的CGLib代理。

我们并不用定义哪个Fruit接口。

但是你如果脾气倔,非要用jdk代理的话。

dd9d42772e579bb4292febd3214202e4.png

把这个加上就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的入门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值