动态代理是一种设计模式。在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的入门

最新文章

  1. SQL Server 2008 数据库镜像部署实例之二 配置镜像,实施手动故障转移
  2. cURL和HTTPie
  3. java运算符总结
  4. ORACLE 对用户密码做限制
  5. java线程中生产者与消费者的问题
  6. Linux netstat命令参数解释
  7. 十四、C# 支持标准查询运算符的集合接口
  8. oc 多线程UI更新
  9. Css 应用一
  10. C语言中为什么不能把char**赋给const char**
  11. Hadoop学习之配置Eclipse远程调试Hadoop
  12. Maven注意事项
  13. node.js代理设置
  14. 自定义圆形的ProgressBar
  15. abap test seam 和 TEST-INJECTION
  16. (三)orcale体系结构
  17. CentOS 7安装Python3.5
  18. 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组
  19. docker mysql Exit 1
  20. 牛客网 PAT 算法历年真题 1001 : A+B和C (15)

热门文章

  1. 交叉编译QT 5.6.2 Shell脚本
  2. 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)
  3. cocos creator 事件
  4. 深入Java源码剖析之字符串常量
  5. Net微信网页开发之使用微信JS-SDK获取当前地理位置
  6. Codeforces 337D
  7. stage_ros的world文件配置方法
  8. [Error]syntaxerror: non-ascii character &#39;/xd6&#39; in file
  9. P2059 [JLOI2013]卡牌游戏 概率DP
  10. codeforces 801 D. Volatile Kite(数学题)