Java中静态代理与JDK动态代理

代理模式目的:

代理就是对目标方法(例如业务的处理)进行增强(例如增删改中事务的开启与提交),让专业的人干专业的事情。实现解耦过程,让业务层只关心业务,而无需考虑例如事务的开启与提交,日志管理等等这些增强的动作我都不关注了,在业务类里只写业务!最后在代理类里把增强的动作和目标动作结合起来就ok了。

静态代理缺点

静态代理是程序运行前,代理类的.class文件已经存在了。

静态代理的缺点:

1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要创建100个代理类

2、如果一个Service中有很多方法需要事务(增强动作),发现代理对象的方法中还是有很多重复的代码

3、由第一点和第二点可以得出:静态代理的重用性不强
动态代理实现的目的和静态代理一样,都是对目标方法进行增强,而且让增强的动作和目标动作分开,达到解耦的目的

例子:

1、方法的接口

package com.cj.study.proxy;
 
public interface PersonService {
	
	public void savePerson();
	
	public void updatePerson();
	
	public void deletePerson();
	
}

2、目标实现类(只关注业务)

package com.cj.study.proxy;
 
public class PersonServiceImpl implements PersonService{
 
	@Override
	public void savePerson() {
		System.out.println("添加");
	}
 
	@Override
	public void updatePerson() {
		System.out.println("修改");
	}
 
	@Override
	public void deletePerson() {
		System.out.println("删除");
	}
 
}

3、增强类(例如开启事务提交事务与业务无关等方法封装在这个类中)

package com.cj.study.proxy;
 
public class Transaction {
	public void beginTransaction(){
		System.out.println("开启事务 ");
	}
	public void commit(){
		System.out.println("提交事务");
	}
}

4、写一个代理类,与目标类实现同样的接口

需要目标类与增强类的对象(本例子也可以使用以下是用构造器方法来注入对象)

以下方式是配置xml文件利用<bean>标签(作用用于配置对象让spring来创建的

)的方式注入对象,重写同样的方法,调用增强类对象与目标类对象中的方法就

OK啦

package com.cj.study.proxy;
 
public class PersonServiceProxy implements PersonService{
	
    
	//目标类
	private PersonService personService;
	
	//增强类
	private Transaction transaction;
	
	@Override
	public void savePerson() {
		transaction.beginTransaction();
		personService.savePerson();
		transaction.commit();
	}
 
	@Override
	public void updatePerson() {
		transaction.beginTransaction();
		personService.updatePerson();
		transaction.commit();
	}
 
	@Override
	public void deletePerson() {
		transaction.beginTransaction();
		personService.deletePerson();
		transaction.commit();
	}
 
}

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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
	<bean id="personServie" class="com.cj.study.proxy.PersonServiceImpl"></bean>
	
	<bean id="transaction" class="com.cj.study.proxy.Transaction"></bean>
	
	<bean id="personServiceProxy" class="com.cj.study.proxy.PersonServiceProxy">
		<constructor-arg index="0" ref="personServie"></constructor-arg>
		<constructor-arg index="1" ref="transaction"></constructor-arg>
	</bean>
	
</beans>

测试:

package com.cj.study.proxy;
 
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class ProxyTest {
	@Test
	public void test(){
		ApplicationContext context = new ClassPathXmlApplicationContext("com/cj/study/proxy/applicationContext-proxy.xml");
		PersonService personService = (PersonService)context.getBean("personServiceProxy");
		personService.savePerson();
	}
}

动态代理

动态代理是,在程序运行时运用反射机制动态创建而成,无需手动编写代码

动态代理类别

动态代理分为JDK的动态代理和cglib动态代理

它俩有略微的差别:JDK动态代理产生的代理类和目标类实现了相同的接口;cglib动态代理产生的代理类是目标对象的子类

JDK的动态代理

1、同样的接口

package com.cj.study.proxyjdk;
 
public interface PersonService {
	
	public String savePerson();
	
	public void updatePerson();
	
	public void deletePerson();
	
}

2、同样的目标类

package com.cj.study.proxyjdk;
 
public class PersonServiceImpl implements PersonService{
 
	@Override
	public String savePerson() {
		System.out.println("添加");
		return "保存成功!";
	}
 
	@Override
	public void updatePerson() {
		System.out.println("修改");
	}
 
	@Override
	public void deletePerson() {
		System.out.println("删除");
	}
 
}

3、同样的增强类

package com.cj.study.proxyjdk;
 
public class MyTransaction {
	public void beginTransaction(){
		System.out.println("开启事务 ");
	}
	public void commit(){
		System.out.println("提交事务");
	}
}

4、首先我们需要只要在JDK的API中有一个名为Proxy的类,其中有一个名为

Object newProxyInstance

(ClassLoader loader,Class<?>[] interfaces,InvocationHandle h)

的方法

loader参数是目标类的类加载器,通过反射获得

interfaces参数是目标类实现的接口,通过反射获得,因为可能有多个,所以是一个数组

h参数是一个拦截器的接口,这个拦截器需要我们自己去实现,利用多态的思想传入拦截器对象

5、编写拦截器,实现newProxyInstance方法中的InvocationHandle接口

用的构造器方法注入目标类与增强类的对象

package com.cj.study.proxyjdk;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class PersonServiceInterceptor implements InvocationHandler{
	//目标类
	private Object target;
	//增强类
	private MyTransaction myTransaction;
	//构造函数注入目标类和增强类
	public PersonServiceInterceptor(Object target,MyTransaction myTransaction){
		this.target = target;
		this.myTransaction = myTransaction;
	}
 
	//代理类的每一个方法被调用的时候都会调用下边的这个invoke方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		this.myTransaction.beginTransaction();
		Object returnValue = method.invoke(this.target, args);
		this.myTransaction.commit();
		return returnValue;
	}
	
}

6、我们发现InvocationHandle这个接口中有一个invoke的方法

public Object invoke(Object proxy, Method method, Object[] args)

这个方法在newProxyInstance返回代理对象,代理对象调用方法时,都会调用下面的invoke()方法

所以在invoke()方法内要调用好增强类方法中该做的事情,与利用反射机制动态的调用目标类的方法。

它的三个参数分别是

invoke三个参数:

proxy:就是代理对象,newProxyInstance方法的返回对象,proxy参数传递的即是代理类的实例

method:调用的方法,你在测试中利用返回的代理类调用的是哪个方法,他就是哪个方法的method(就是反射里的那个Method类)

args: 方法中的参数 就是调用方法里的参数

1、当客户端执行代理对象.方法时,进入到了拦截器的invoke方法体

2、拦截器中invoke方法体的内容就是代理对象方法体的内容

3、拦截器中invoke方法的method参数是在调用的时候赋值的

7、我们会发现在拦截器的invoke()方法当中还有一个method.invoke()方法,这个方法是反射里面的,里面有两个参数

一是传入的对象,二是方法的实参。

具体可以跳到另一篇博客去简单的理解反射

https://mp.csdn.net/mp_blog/creation/editor/122168924https://mp.csdn.net/mp_blog/creation/editor/122168924https://mp.csdn.net/mp_blog/creation/editor/122168924

8、测试

package com.cj.study.proxyjdk;
 
import java.lang.reflect.Proxy;
import org.junit.Test;
 
public class ProxyTest {
	@Test
	public void test(){
		Object target = new PersonServiceImpl();
		MyTransaction myTransaction = new MyTransaction();
		PersonServiceInterceptor interceptor = new PersonServiceInterceptor(target, myTransaction);
		PersonService personService = (PersonService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),interceptor);
		String returnValue = (String)personService.savePerson();
		System.out.println(returnValue);
	}
}

就实现了利用JDK动态代理AOP面向切面编程。

那怎么实现只针对某个接口里的某个方法拦截,而不是针对接口里所有方法都拦截呢?

只需要在调用invoke方法里,method调用前,加个if判断嘛,根据method,getName().equal(“具体方法”)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值