Spring学习之JDK代理和Gclib代理

Spring学习之JDK代理和Gclib代理

在Spring框架中,使用JDK动态代理和CGLIB代理是为了支持面向切面编程(AOP)。Spring AOP默认会根据目标bean是否实现接口来选择使用JDK动态代理还是CGLIB代理。这个选择过程是自动的,但您也可以通过配置来显式指定使用哪一种代理方式。

JDK动态代理

JDK动态代理主要用于代理实现了接口的对象。在Spring中,默认情况下,如果目标bean实现了至少一个接口,Spring AOP会使用JDK动态代理。

如何使用
  1. 定义接口:创建一个或多个接口,定义需要代理的方法。
  2. 实现接口:创建类实现这些接口。
  3. 启用AOP:在Spring配置中启用AOP。
package com.phl.aop.pojo;

public interface IUser {
    
    

    void pay();
}


package com.phl.aop.pojo;

import org.springframework.stereotype.Component;

@Component
public class User implements IUser{
    
    

    /**
     * 支付功能
     */
    public void pay(){
    
    
        System.out.println("用户进行了支付");
    }
}
package com.phl.aop.pojo;

public interface IStudent {
    
    

    String study();
}



package com.phl.aop.pojo;

import org.springframework.stereotype.Component;

@Component
public class Student implements IStudent{
    
    

    @Override
    public String study() {
    
    
        System.out.println("同学进行了Spring的课程学习");
        return "学到了Spring的知识点";
    }

    public void say_hello(){
    
    
        System.out.println("SayHello");
    }

}

CGLIB代理

如果目标bean没有实现任何接口,Spring AOP会自动使用CGLIB来创建代理。CGLIB代理适用于代理普通的Java类,因为它是通过子类化的方式来实现代理的。

如何使用
  1. 创建类:创建一个普通的Java类,不需要实现任何接口。
  2. 启用AOP:在Spring配置中启用AOP。
package com.phl.aop.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Employee {
    
    

    @Value("1001")
    private int empId;
    @Value("Tom")
    private String empName;

    public void details(){
    
    
        System.out.println("员工信息,工号:"+empId+",姓名:"+empName);
    }
}

测试

import com.phl.aop.pojo.*;
import com.phl.aop.proxy.CglibProxy;
import com.phl.aop.proxy.JdkProxy;
import com.phl.aop.proxy.UserProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring.xml")
public class SpringTest {
    
    
    //静态代理类
    @Resource
    private UserProxy userProxy;

    //JDK动态代理类
    @Resource
    private JdkProxy jdkProxy;

    @Resource
    private IUser user;

    @Resource
    private IStudent student;
    @Resource
    private CglibProxy cglibProxy;

    @Resource
    private Employee employee;
    //@Test
    public void test1(){
    
    
        userProxy.pay();
    }

    //@Test
    public void test2(){
    
    
//        //生成代理类,代理类实现了代理目标的接口
//        IUser u = (IUser) jdkProxy.createProxy(user);
//        //获取类的类名
//        //System.out.println(u.getClass().getName());
//        u.pay();

        //生成Student类的代理类
        IStudent stu = (IStudent) jdkProxy.createProxy(student);
        System.out.println(stu.getClass().getName());
        String studyResult = stu.study();
        System.out.println("学习成果:"+studyResult);
    }

    //@Test
    public void test3(){
    
    
        Student stu = (Student) cglibProxy.createProxy(student);
        stu.study();
        //stu.say_hello();
    }

    @Test
    public void test4(){
    
    
        user.pay();
        student.study();
        employee.details();
    }
}

在上面代码中,自动装配时,对象user和student的类型都必须是接口类型,使用的是JDK代理,因为Student和User实现了IStudent和IUser接口,spring会默认生成JDK代理

而Employee的对象并没有实现接口,所以默认是Cglib代理

通知Advice

package com.phl.aop.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * 方法执行后拦截的通知
 */
public class MyAfterAdvice implements AfterReturningAdvice {
    
    

    /**
     *
     * @param returnValue 原方法执行后的返回值
     * @param method 原方法
     * @param args 方法所需的参数
     * @param target 代理目标
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println(target.getClass().getName()+
                "类的"+method.getName()+"方法执行结束,返回值是:"+returnValue);
    }
}
package com.phl.aop.advice;

import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * 自定义方法执行前通知
 * 用于对方法执行前进行拦截处理
 * 并封装了扩展的业务代码
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    
    

    /**
     *
     * @param method 拦截的原方法
     * @param args 方法所需的参数
     * @param target 代理目标
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    

        System.out.println(target.getClass().getName()+"类的"+method.getName()+"准备执行");
    }
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <context:component-scan base-package="com.phl.aop.pojo,com.phl.aop.proxy"> </context:component-scan>

    <!--   配置通知-->
    <bean id="beforeAdvice" class="com.phl.aop.advice.MyBeforeAdvice"> </bean>
    <bean id="afterAdvice" class="com.phl.aop.advice.MyAfterAdvice"> </bean>

    <!--   配置Spring的自动代理类-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
       <!--      配置代理目标-->
       <!--      写入代理目标在Spring容器中的id-->
       <!--      允许批量生成多个目标的代理对象-->
       <property name="beanNames">
          <list>
             <value>user</value>
             <value>student</value>
             <value>employee</value>
          </list>
       </property>
       <!--      配置通知-->
       <!--      写入在Spring容器中管理的通知对象的id-->
       <property name="interceptorNames">
          <list>
<!--             <value>beforeAdvice</value>-->
             <value>afterAdvice</value>
             <!--            <value>afterAdvice</value>-->
          </list>
       </property>
    </bean>
</beans>

在Spring框架的配置文件中,特别是在使用自动代理创建器如BeanNameAutoProxyCreator时,beanNames属性用于指定哪些Spring beans应该被自动代理。这是一种基于bean名称的自动代理方法,常用于面向切面编程(AOP)。

用途

  • 指定代理目标:通过beanNames属性,可以精确指定哪些bean需要创建代理。只有名称与beanNames列表中的名称匹配的bean才会被代理。
  • 应用Advice:对于这些被代理的bean,将会应用在配置文件中指定的通知(advice),例如方法前后执行的操作。

示例解释

在配置文件中,BeanNameAutoProxyCreator配置如下:

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
        <list>
            <value>user</value>
            <value>student</value>
            <value>employee</value>
        </list>
    </property>
    <!-- 其他配置 -->
</bean>

在这个配置中:

扫描二维码关注公众号,回复: 17289420 查看本文章
  • beanNames属性指定了一个bean名称列表。在这个例子中,包括userstudentemployee
  • 任何在Spring上下文中名称为userstudentemployee的bean都将被BeanNameAutoProxyCreator自动代理。
  • 这意味着,当这些bean中的任何一个执行方法时,配置的通知(如beforeAdvice)将会被应用。

使用场景

BeanNameAutoProxyCreator通常用于在不修改现有代码的情况下,为特定的bean添加额外的行为(如安全检查、事务管理、日志记录等)。这是实现AOP的一种方式,它使得你可以在运行时动态地为对象添加额外的处理逻辑,而调用者无需知晓这一点。

注意事项

  • 此种配置方式适用于当你想要对特定的一些bean应用通知,而不是基于类型或其他标准。
  • 如果有多个bean具有相同的名称或符合模式,它们都将被代理。
  • 确保beanNames中指定的名称与Spring上下文中定义的bean名称一致。

猜你喜欢

转载自blog.csdn.net/Johnor/article/details/134575667