spring基礎知識---AOP動態代理原理

Spring Boot實踐——Spring AOP實現之動態代理

Spring AOP 介紹

  AOP的介紹能夠查看 Spring Boot實踐——AOP實現html

  與AspectJ的靜態代理不一樣,Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改字節碼,而是在內存中臨時爲方法生成一個AOP對象,這個AOP對象包含了目標對象的所有方法,而且在特定的切點作了加強處理,並回調原對象的方法。java

  Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理。JDK動態代理經過反射來接收被代理的類,而且要求被代理的類必須實現一個接口。JDK動態代理的核心是InvocationHandler接口和Proxy類。spring

  若是目標類沒有實現接口,那麼Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,是利用asm開源包,能夠在運行時動態的生成某個類的子類。注意,CGLIB是經過繼承的方式作的動態代理,所以若是某個類被標記爲final,那麼它是沒法使用CGLIB作動態代理的。springboot

  這裏有注意的幾點以下:app

  • 從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。

 

實現方式

1、驗證Spring AOP動態代理

博主使用的是Spring Boot 2.0版本框架

一、JDK動態代理

定義接口less

public interface Person {
    String say(String name);
    
    void eat(String food);
}

實現類ide

複製代碼

@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);
    }
    
}

複製代碼

定義Aspectthis

複製代碼

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

複製代碼

 

二、CGLIB動態代理

定義一個類spa

複製代碼

@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);
    }
    
}

複製代碼

三、配置

<!-- 自動掃描使用了aspectj註解的類 -->
<aop:aspectj-autoproxy/>

@Configuration
@ComponentScan("com.only.mate.springboot.aop")
@EnableAspectJAutoProxy//開啓AspectJ註解
public class CustomAopConfigurer {
}

四、運行

複製代碼

@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";
    }
}

複製代碼

五、效果圖

問題出現了,按照以前的說法,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動態代理。失效的具體緣由有待查驗!

2、Spring AOP動態代理實現

一、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;
    }
}

複製代碼

二、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;
    }
}

複製代碼

 

三、運行

複製代碼

@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";
        
    }

}

複製代碼

四、效果圖

簡單的實現了Spring AOP的JDK動態代理和CGLIB動態代理。

What a meaningless sense if losing myself,though owning all of the world.

相關文章
相關標籤/搜索