Spring Boot实践——Spring AOP实现之动态代理
Spring AOP 介绍
AOP的介绍可以查看 Spring Boot实践——AOP实现
与AspectJ的静态代理不同,Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,是利用asm开源包,可以在运行时动态的生成某个类的子类。注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
这里有注意的几点如下:
- 从Spring 3.2以后不再将CGLIB放在项目的classpath下,而是将CGLIB类打包放在spring-core下面的org.springframework中。这个就意味着基于CGLIB的动态代理与JDK的动态代理在支持“just works”就一样了。
- 在Spring 4.0中,因为CGLIB代理实例是通过Objenesis创建的,所以代理对象的构造器不再有两次调用。
- 在 Spring Boot 2.0 中,Spring Boot现在默认使用CGLIB动态代理(基于类的动态代理), 包括AOP. 如果需要基于接口的动态代理(JDK基于接口的动态代理) , 需要设置spring.aop.proxy-target-class属性为false。
实现方式
一、验证Spring AOP动态代理
博主使用的是Spring Boot 2.0版本
1、JDK动态代理
定义接口
public interface Person {
String say(String name);
void eat(String food);
}
实现类
@Component
public class Chinese implements Person {
private Logger logger = LoggerFactory.getLogger(Person.class); public Chinese() {
super();
logger.info("Chinese ==> Chinese method : 正在生成一个Chinese实例");
} @Override
@PersonAnnotation(name="Chinese")//该注解是用来定义切点
public String say(String name) {
logger.info("Chinese ==> say method : say {}", name);
return name + " hello, JDK implement AOP";
} @Override
public void eat(String food) {
logger.info("Chinese ==> eat method : eat {}", food);
} }
定义Aspect
@Aspect
@Component
public class PersonAspect {
private Logger logger = LoggerFactory.getLogger(PersonAspect.class); @Pointcut("@annotation(com.only.mate.springboot.annotation.PersonAnnotation)")
public void pointCut(){ } @Before("pointCut()")
public void before(JoinPoint joinPoint) throws Throwable {
logger.info("PersonAspect ==> before method : {}", joinPoint.getClass()); } @After("pointCut()")
public void after(JoinPoint joinPoint){
logger.info("PersonAspect ==> after method : {}", joinPoint.getClass());
}
}
2、CGLIB动态代理
定义一个类
@Component
public class American {
private Logger logger = LoggerFactory.getLogger(American.class); public American() {
super();
logger.info("American ==> American method : 正在生成一个American实例");
} @PersonAnnotation(name="American")//该注解是用来定义切点
public String say(String name) {
logger.info("American ==> say method : say {}", name);
return name + " hello, CGLIB implement AOP";
} public void eat(String food) {
logger.info("American ==> eat method : eat {}", food);
} }
3、配置
<!-- 自动扫描使用了aspectj注解的类 -->
<aop:aspectj-autoproxy/>
或
@Configuration
@ComponentScan("com.only.mate.springboot.aop")
@EnableAspectJAutoProxy//开启AspectJ注解
public class CustomAopConfigurer {
}
4、运行
@Controller
@RequestMapping(value="/aop")
public class AopImplementController {
private Logger logger = LoggerFactory.getLogger(AopImplementController.class); @Autowired
private Person chinese;
@Autowired
private American american; @ResponseBody
@RequestMapping(value="/talk")
public String talk() {
chinese.say("中国人说汉语");
american.say("American say english"); logger.info("AopImplementController ==> talk method : {}", chinese.getClass());
logger.info("AopImplementController ==> talk method : {}", american.getClass()); return "success";
}
}
5、效果图

问题出现了,按照之前的说法,Chinese应该是用JDK动态代理,American使用CGLIB动态代理才对。
由于博主使用的是Spring Boot 2.0 ,在 Spring Boot 2.0 中,Spring Boot现在默认使用CGLIB动态代理(基于类的动态代理), 包括AOP。 如果需要基于接口的动态代理(JDK基于接口的动态代理) ,需要设置spring.aop.proxy-target-class属性为false。
因此在application.properties加上配置spring.aop.proxy-target-class=false。重启访问
如图:

这样才达到了预期的目的
通过网上查找:
- 想要强制使用CGLIB,那么就设置
<aop:config>下面的proxy-target-class属性为true
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>
此处没有验证,仅供参考。
- 要是使用@AspectJ强制使用CGLIB的话,可以配置
<aop:aspectj-autoproxy>下的proxy-target-class属性为true
<aop:aspectj-autoproxy proxy-target-class="true"/>
此处博主用Spring Boot 2.0,加以上配置无效,还是使用默认的CGLIB动态代理。失效的具体原因有待查验!
- 要是使用@AspectJ强制使用CGLIB的话,向
@EnableAspectJAutoProxy注解中添加属性proxyTargetClass = true
此处博主用Spring Boot 2.0,加以上配置无效,还是使用默认的CGLIB动态代理。失效的具体原因有待查验!
二、Spring AOP动态代理实现
1、JDK动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* @Description: Spring AOP之JDK动态代理实现
* @ClassName: JDKDynamicSubject
* @author OnlyMate
* @Date 2018年9月11日 下午4:47:53
*
*/
public class JDKDynamicObject implements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(JDKDynamicObject.class); private Object target; public JDKDynamicObject() {
} /**
* @Description: 绑定对象,并生成代理对象
* @Title: bind
* @author OnlyMate
* @Date 2018年9月11日 下午4:48:31
* @param target
* @return
*/
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 {
logger.info("JDKDynamicObject ==> invoke method : {},{},{}", proxy.getClass(), method.getName(),
args.toString());
method.invoke(target, args);
return null;
}
}
2、CGLIB动态代理
import java.lang.reflect.Method; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler; /**
* @Description: Spring AOP之CGLIB动态代理实现
* @ClassName: CGLIBDynamicObject
* @author OnlyMate
* @Date 2018年9月11日 下午4:57:30
*
*/
public class CGLIBDynamicObject implements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(CGLIBDynamicObject.class); private Object target; public CGLIBDynamicObject() {
} /**
* @Description: 绑定对象,并生成代理对象
* @Title: bind
* @author OnlyMate
* @Date 2018年9月11日 下午4:48:31
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
} @Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
logger.info("CGLIBDynamicObject ==> invoke method : {},{},{}", arg0.getClass(), arg1.getName(),
arg2.toString());
arg1.invoke(target, arg2);
return null;
}
}
3、运行
@Controller
@RequestMapping(value="/aop")
public class AopImplementController {
private Logger logger = LoggerFactory.getLogger(AopImplementController.class); @Autowired
private Person chinese;
@Autowired
private American american; @ResponseBody
@RequestMapping(value="/talk")
public String talk() {
// chinese.say("中国人说汉语");
// american.say("American say english");
//
// logger.info("AopImplementController ==> talk method : {}", chinese.getClass());
// logger.info("AopImplementController ==> talk method : {}", american.getClass()); //自定义JDK动态代理
// Chinese chinese1 = new Chinese();
// InvocationHandler dsc = new JDKDynamicObject(chinese1);
// Person person = (Person) Proxy.newProxyInstance(chinese1.getClass().getClassLoader(), chinese1.getClass().getInterfaces(), dsc);
// person.say("中国人说汉语");
// logger.info("AopImplementController ==> talk method : JDKDynamicObject {}", person.getClass()); JDKDynamicObject dsc1 = new JDKDynamicObject();
Person person1 = (Person)dsc1.bind(new Chinese());
person1.say("中国人说汉语");
logger.info("AopImplementController ==> talk method : JDKDynamicObject {}", person1.getClass()); //自定义CGLIB动态代理
CGLIBDynamicObject dsm = new CGLIBDynamicObject();
American american1 = (American) dsm.bind(new American());
american1.say("American say english");
logger.info("AopImplementController ==> talk method : CGLIBDynamicObject {}", american1.getClass());
return "success"; } }
4、效果图

简单的实现了Spring AOP的JDK动态代理和CGLIB动态代理。
Spring Boot实践——Spring AOP实现之动态代理的更多相关文章
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
- Spring Boot实践——Spring Boot 2.0 新特性和发展方向
出自:https://mp.weixin.qq.com/s/EWmuzsgHueHcSB0WH-3AQw 以Java 8 为基准 Spring Boot 2.0 要求Java 版本必须8以上, Jav ...
- Spring Boot实践——AOP实现
借鉴:http://www.cnblogs.com/xrq730/p/4919025.html https://blog.csdn.net/zhaokejin521/article/detai ...
- Spring Boot中使用AOP统一处理Web请求日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...
- Spring Boot中使用AOP记录请求日志
这周看别人写的springboot后端代码中有使用AOP记录请求日志,以前没接触过,因此学习下. 一.AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编 ...
- 46. Spring Boot中使用AOP统一处理Web请求日志
在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...
- Spring Boot 实践 :Spring Boot + MyBatis
Spring Boot 实践系列,Spring Boot + MyBatis . 目的 将 MyBatis 与 Spring Boot 应用程序一起使用来访问数据库. 本次使用的Library spr ...
- Spring AOP中的动态代理
0 前言 1 动态代理 1.1 JDK动态代理 1.2 CGLIB动态代理 1.2.1 CGLIB的代理用法 1.2.2 CGLIB的过滤功能 2 Spring AOP中的动态代理机制 2.1 ...
- 基于Spring Boot、Spring Cloud、Docker的微服务系统架构实践
由于最近公司业务需要,需要搭建基于Spring Cloud的微服务系统.遍访各大搜索引擎,发现国内资料少之又少,也难怪,国内Dubbo正统治着天下.但是,一个技术总有它的瓶颈,Dubbo也有它捉襟见肘 ...
随机推荐
- 如何高效利用 GitHub
正是 Github,让社会化编程成为现实.本文尝试谈谈 GitHub 的文化.技巧与影响. Q1:GitHub 是什么 Q2:GitHub 风格 Q3: 在 GitHub,如何跟牛人学习 Q4: 享受 ...
- 115. Distinct Subsequences *HARD* -- 字符串不连续匹配
Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...
- Springfox与swagger的整合使用(十七)
一.前言 让我们先理一下springfox与swagger的关系. swagger是一个流行的API开发框架,这个框架以“开放API声明”(OpenAPI Specification,OAS)为基础, ...
- 将Gridview中的数据出到excel或word中
在以下按钮单击事件中实现:private void btnMIME_Click(object sender, System.EventArgs e){dgShow.AllowPaging = fals ...
- -webkit新属性 image-set和srcset
响应式图片的作用: 为使用不同分辨率的不同浏览器用户提供适合其浏览环境的图片大小的解决方案. 之前的解决方法是使用@media 但是-webkit新提出的image-set和srcset同样可以解决问 ...
- java基础第6天
面向对象 当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高.可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始改进,能不能把这些步骤和功能再进行封装,封装时根据不 ...
- JS字符串的问题
首先,搞了好几个小时,头都大了,原来出在字符串问题上. 具体如下: 今天做Yii,遇到用JQuery 的AJAX方法做注册验证,把传回来的字符串与textField中的比较,发现相等,但是就是不出结果 ...
- BZOJ4886: [Lydsy1705月赛]叠塔游戏(环套树森林&贪心)
4886: [Lydsy1705月赛]叠塔游戏 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 198 Solved: 76[Submit][Stat ...
- BZOJ4025: 二分图【线段树分治】【带撤销的并查集】
Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. Input ...
- oracle 之 安装10.2.0.1 且 升级到 10.2.0.4
一. centos 6.5 安装 oracle 10.2.0.1 1.安装操作系统,选择桌面环境 2.配置本地yum源 , 可参考 http://blog.csdn.net/zhang12345645 ...