在Spring框架中,使用JDK动态代理和CGLIB代理是为了支持面向切面编程(AOP)。Spring AOP默认会根据目标bean是否实现接口来选择使用JDK动态代理还是CGLIB代理。这个选择过程是自动的,但您也可以通过配置来显式指定使用哪一种代理方式。
JDK动态代理主要用于代理实现了接口的对象。在Spring中,默认情况下,如果目标bean实现了至少一个接口,Spring AOP会使用JDK动态代理。
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");
}
}
如果目标bean没有实现任何接口,Spring AOP会自动使用CGLIB来创建代理。CGLIB代理适用于代理普通的Java类,因为它是通过子类化的方式来实现代理的。
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
<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>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>uservalue>
<value>studentvalue>
<value>employeevalue>
list>
property>
<property name="interceptorNames">
<list>
<value>afterAdvicevalue>
list>
property>
bean>
beans>
在Spring框架的配置文件中,特别是在使用自动代理创建器如BeanNameAutoProxyCreator
时,beanNames
属性用于指定哪些Spring beans应该被自动代理。这是一种基于bean名称的自动代理方法,常用于面向切面编程(AOP)。
beanNames
属性,可以精确指定哪些bean需要创建代理。只有名称与beanNames
列表中的名称匹配的bean才会被代理。在配置文件中,BeanNameAutoProxyCreator
配置如下:
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>uservalue>
<value>studentvalue>
<value>employeevalue>
list>
property>
bean>
在这个配置中:
beanNames
属性指定了一个bean名称列表。在这个例子中,包括user
、student
和employee
。user
、student
或employee
的bean都将被BeanNameAutoProxyCreator
自动代理。beforeAdvice
)将会被应用。BeanNameAutoProxyCreator
通常用于在不修改现有代码的情况下,为特定的bean添加额外的行为(如安全检查、事务管理、日志记录等)。这是实现AOP的一种方式,它使得你可以在运行时动态地为对象添加额外的处理逻辑,而调用者无需知晓这一点。
beanNames
中指定的名称与Spring上下文中定义的bean名称一致。