likes
comments
collection
share

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

作者站长头像
站长
· 阅读数 52

动态代理

1. 为什么使用代理?

在Java中,动态代理是一种强大的工具,可以在运行时动态地为对象创建代理,以添加或修改其行为。通过一个租房场景,我们可以形象地理解动态代理的应用。

1.1 场景描述:

在租房场景中,我们有两个主要的角色:

  1. 房东:这是业务类,负责核心功能,如出租房屋。
  2. 房客:这是服务类,代表调用者,他们需要租房,并且期望某些附加服务。

除了核心的出租房屋功能外,还有一些附加功能需要执行,比如:

  • 张贴广告:让更多人知道房屋出租信息。
  • 带人看房:带领潜在租客参观房子。

1.2 问题与矛盾:

  • 房东的立场:房东只想执行核心功能(如签合同、收租金),不想花时间和精力处理附加功能(如张贴广告、带人看房)。
  • 房客的需求:房客希望得到完整的服务,包括看房和详细的租房信息。

这就产生了一个矛盾:房东不愿意处理附加功能,而房客希望得到这些服务。在软件设计中,这类似于开发者不愿意在核心业务逻辑中加入额外的功能代码,而调用者却希望这些功能能够实现。

1.3 引入代理类:

为了解决这一矛盾,我们可以引入一个代理类。在现实生活中,这个代理角色就像是中介

  1. 中介(代理)的职责:代理类负责处理房东不想做的附加功能,比如张贴广告、带人看房。核心功能(如签合同、收租金)仍然由房东来执行。
  2. 代理模式的优势
    • 功能分离:房东专注于核心功能,代理类处理附加功能,这样可以清晰地分离核心业务逻辑和附加功能。
    • 灵活性:如果房客不满意当前的代理(比如提供的附加服务不够好),可以更换代理而不需要修改房东的代码。类似地,在软件设计中,可以通过引入或替换代理类来增加新的功能,而不涉及核心业务代码的修改。
Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

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 代码演示

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核
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 问题所在

  1. 类文件数量过多,大型项目中不利于项目管理。

  2. 额外功能的维护麻烦。

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

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

3.5 细节分析

Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失。 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。

动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影响项目管理的问题。

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。

<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查看三个参数。

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

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"));
    }
}
Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核 Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

dubug分析:

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核

5. CGlib动态代理

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

Java中的动态代理想让你的代码自动干活?学会用Java动态代理,像“中介”一样,帮你完美解决业务和附加功能的矛盾,让核
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
评论
请登录