Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核
动态代理
1. 为什么使用代理?
在Java中,动态代理是一种强大的工具,可以在运行时动态地为对象创建代理,以添加或修改其行为。通过一个租房场景,我们可以形象地理解动态代理的应用。
1.1 场景描述:
在租房场景中,我们有两个主要的角色:
- 房东:这是业务类,负责核心功能,如出租房屋。
- 房客:这是服务类,代表调用者,他们需要租房,并且期望某些附加服务。
除了核心的出租房屋功能外,还有一些附加功能需要执行,比如:
- 张贴广告:让更多人知道房屋出租信息。
- 带人看房:带领潜在租客参观房子。
1.2 问题与矛盾:
- 房东的立场:房东只想执行核心功能(如签合同、收租金),不想花时间和精力处理附加功能(如张贴广告、带人看房)。
- 房客的需求:房客希望得到完整的服务,包括看房和详细的租房信息。
这就产生了一个矛盾:房东不愿意处理附加功能,而房客希望得到这些服务。在软件设计中,这类似于开发者不愿意在核心业务逻辑中加入额外的功能代码,而调用者却希望这些功能能够实现。
1.3 引入代理类:
为了解决这一矛盾,我们可以引入一个代理类。在现实生活中,这个代理角色就像是中介:
- 中介(代理)的职责:代理类负责处理房东不想做的附加功能,比如张贴广告、带人看房。核心功能(如签合同、收租金)仍然由房东来执行。
- 代理模式的优势:
- 功能分离:房东专注于核心功能,代理类处理附加功能,这样可以清晰地分离核心业务逻辑和附加功能。
- 灵活性:如果房客不满意当前的代理(比如提供的附加服务不够好),可以更换代理而不需要修改房东的代码。类似地,在软件设计中,可以通过引入或替换代理类来增加新的功能,而不涉及核心业务代码的修改。

2. 代理设计模式
通过代理类,为原始类(目标类)增加额外的功能。好处就是便于原始类的维护。
1. 目标类,原始类指的是业务类,业务处理+Dao调用。
2. 目标方法,原始方法指的是原始类中的方法。
3. 额外功能就是附加的功能,比如日志,事务等。
2.1 代理开发的核心要素
代理类 = 目标类(原始类)+ 额外功能 + 原始类(目标类)实现相同的接口
房东 -->
public interface UserService{
m1
m2
}
UserServiceImpl implements UserService{
m1--->业务运算DAO调用
m2
}
UserServiceProxy implements UserService{
(额外功能)
m1
m2
}
2.2 代码演示

public class User {
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
}
public interface UserService {
void register(User user);
boolean login(String username, String password);
}
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务处理 + Dao操作");
}
@Override
public boolean login(String username, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
public interface OrderService {
void showOrder();
}
public class OrderServiceImpl implements OrderService {
@Override
public void showOrder() {
System.out.println("OrderServiceImpl.showOrder");
}
}
代理类:
public class UserServiceProxy implements UserService {
private UserServiceImpl userServiceImpl = new UserServiceImpl();
@Override
public void register(User user) {
System.out.println("---log---");
userServiceImpl.register(user);
}
@Override
public boolean login(String username, String password) {
System.out.println("---log---");
return userServiceImpl.login(username, password);
}
}
public class OrderServiceProxy implements OrderService {
private OrderServiceImpl orderServiceImpl = new OrderServiceImpl();
@Override
public void showOrder() {
System.out.println("---log--");
orderServiceImpl.showOrder();
}
}
测试程序:
@Test
public void test() {
UserService userService = new UserServiceProxy();
userService.register(new User("han", "123456"));
userService.login("han", "1234566");
System.out.println("---------------------------------------");
OrderService orderService = new OrderServiceProxy();
orderService.showOrder();
}
---log---
UserServiceImpl.register 业务处理 + Dao操作
---log---
UserServiceImpl.login
---------------------------------------
---log--
OrderServiceImpl.showOrder
2.3 问题所在
-
类文件数量过多,大型项目中不利于项目管理。
-
额外功能的维护麻烦。
3. Spring的动态代理
通过代理类,为原始类(目标类)增加额外的功能。好处就是便于原始类的维护。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.1.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.22</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
3.1 创建原始对象
public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register 业务处理 + Dao操作");
}
@Override
public boolean login(String username, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
}
3.2 额外功能
实现MethodBeforeAdvice
接口,运行在原始方法之前的额外功能。
public class Before implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("method before advice log");
}
}
3.3 定义整合切入点
切入点就是额外功能加入的位置。程序员编写expression
表达式决定。
<bean id="userService" class="com.lb.proxy.UserServiceImpl"/>
<bean id="before" class="com.lb.dynamic.Before"/>
<aop:config>
<!--所有切入点加入额外功能-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--组装,把切入点和额外功能整合-->
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
</aop:config>
3.4 调用
目的:获得Spring工厂创建的动态代理对象,并进行调用
ApplicationContext ctx =new ClassPathXmlApplicationContext("/applicationContext.xml");
注意:
1.Spring的工厂通过原始对象的id值获得的是代理对象
2.获得代理对象后,可以通过声明接口类型,进行对象的存储
UserService userService=(UserService)ctx.getBean("userService");
userService.login("")
userService.register()
@Test
public void testDynamicProxy() {
ApplicationContext context = new ClassPathXmlApplicationContext("dynamicProxy.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.register(new User("han", "123456"));
userService.login("han", "123456");
}
method before advice log
UserServiceImpl.register 业务处理 + Dao操作
method before advice log
UserServiceImpl.login
3.5 细节分析
Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失。 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。
动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影响项目管理的问题。

在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。
<bean id="orderService" class="com.lb.proxy.OrderServiceImpl"/>
额外功能的维护也更加便捷,Before
不满足要求时,可以重新创建一个NewBefore
并配置即可。
public class NewBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("new method before advice log");
}
}
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.lb.proxy.UserServiceImpl"/>
<bean id="orderService" class="com.lb.proxy.OrderServiceImpl"/>
<!-- <bean id="before" class="com.lb.dynamic.Before"/>-->
<bean id="before" class="com.lb.dynamic.NewBefore"/>
<aop:config>
<!--所有切入点加入额外功能-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--组装,把切入点和额外功能整合-->
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
</aop:config>
</beans>
3.6 MethodBeforeAdvice
详解
public class NewBefore implements MethodBeforeAdvice {
/**
* 调用方法,调用参数,原始对象
* @param method 额外功能所增加给的那个原始方法 login register showOrder
* @param args 额外功能所增加给的那个原始方法的参 name password User
* @param target 额外功能所增加给的那个原始对象 UserServiceImpl OrderServiceImpl
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("new method before advice log");
}
}
可以通过debug查看三个参数。
3.7 MethodInterceptor
详解(方法拦截器)
methodInterceptor
接口:额外功能可以根据需要运行在原始方法执行前、后、前后。
public class Around implements MethodInterceptor {
/**
* 原始方法之前,原始方法之后,原始方法执行之前之后
* @param invocation 外功能所增加给的那个原始方法
* @return 返回值:0bject:原始方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("额外功能开始");
Object result = invocation.proceed();
System.out.println("额外功能结束");
return result;
}
}
同样的更改配置并测试。
<bean id="around" class="com.lb.dynamic.Around"/>
<aop:config>
<!--所有切入点加入额外功能-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!--组装,把切入点和额外功能整合-->
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
</aop:config>
@Test
public void testDynamicProxy() {
ApplicationContext context = new ClassPathXmlApplicationContext("dynamicProxy.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.register(new User("han", "123456"));
userService.login("han", "123456");
OrderService orderService = context.getBean("orderService", OrderService.class);
orderService.showOrder();
}
额外功能开始
UserServiceImpl.register 业务处理 + Dao操作
额外功能结束
额外功能开始
UserServiceImpl.login
额外功能结束
额外功能开始
OrderServiceImpl.showOrder
额外功能结束
额外功能也可以运行在原始方法抛出异常的时候。
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("额外功能开始");
Object result = null;
try {
result = invocation.proceed();
} catch (Throwable throwable) {
System.out.println("原始方法抛出的异常 执行的额外功能");
throwable.printStackTrace();
}
System.out.println("额外功能结束");
return result;
}
Methodlnterceptor
还可以影响原始方法的返回值,Invoke方法的返回值,不要直接返回原始方法的运行结果即可。
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("额外功能开始");
Object result = invocation.proceed();
System.out.println("额外功能结束");
return false;
}
4. JDK动态代理
public class TestJdkProxy {
public static void main(String[] args) {
// 1.创建原始对象
UserService userService = new UserServiceImpl();
// 2.Jdk创建动态代理
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---proxy log---");
Object result = method.invoke(userService, args);
return result;
}
};
UserService proxyInstance = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
invocationHandler);
proxyInstance.login("han", "123456");
proxyInstance.register(new User("han", "123456"));
}
}


dubug分析:
5. CGlib动态代理
CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)。

public class TestCglib {
public static void main(String[] args) {
// 1.创建原始对象
UserService userService = new UserService();
/**
* 2.通过cglib方式创建动态代理对象
* Proxy.newProxyInstance(classloader,interface,invocationhandler)
* Enhancer.setClassLoader()
* Enhancer.setSuperClass()
* Enhancer.setCallback();-->MethodInterceptor(cglib)
* Enhancer.create()--->代理
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(UserService.class.getClassLoader());
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("---cglib log---");
Object result = method.invoke(userService, args);
return result;
}
});
UserService proxy = (UserService) enhancer.create();
proxy.login("han", "123456");
}
}
public class UserService {
public void register(User user) {
System.out.println("UserService.register");
}
public boolean login(String username, String password) {
System.out.println("UserService.login");
return true;
}
}
---cglib log---
UserService.login
转载自:https://juejin.cn/post/7406864715358552114