Spring Boot实践——Spring AOP实现之动态代理

本文内容纲要:

- Spring AOP 介绍

- 实现方式

- 一、验证Spring AOP动态代理

- 1、JDK动态代理

- 2、CGLIB动态代理

- 3、配置

- 4、运行

- 5、效果图

- 二、Spring AOP动态代理实现

- 1、JDK动态代理

- 2、CGLIB动态代理

- 3、运行

- 4、效果图

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、效果图

Image

问题出现了,按照之前的说法,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。重启访问

如图:

Image

这样才达到了预期的目的


通过网上查找:

  • 想要强制使用CGLIB,那么就设置<aop:config>下面的proxy-target-class属性为true

    <aop:config proxy-target-class="true">

    </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、效果图

Image

简单的实现了Spring AOP的JDK动态代理和CGLIB动态代理。

本文内容总结:Spring AOP 介绍,实现方式,一、验证Spring AOP动态代理,1、JDK动态代理,2、CGLIB动态代理,3、配置,4、运行,5、效果图,二、Spring AOP动态代理实现,1、JDK动态代理,2、CGLIB动态代理,3、运行,4、效果图,

原文链接:https://www.cnblogs.com/onlymate/p/9630788.html

以上是 Spring Boot实践——Spring AOP实现之动态代理 的全部内容, 来源链接: utcz.com/z/296837.html

回到顶部