Spring中的动态代理

1.Spring AOP 就是要对目标进行代理对象的创建, Spring AOP是基于动态代理的,有动态代理机制: JDK动态代理和CGLIB动态代理 

动态代理:在虚拟机内部,运行的时候,动态生成代理类(运行时生成,runtime生成) ,并不是真正存在的类,

一般格式:

Proxy$$ (Proxy$$Customer)

静态代理:实际存在代理类 (例如:struts2 Action的代理类 ActionProxy,struts2的拦截器)

 

JDK动态代理

JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 !(必须有接口)

1、 必须对接口生成代理

2、 采用Proxy类,通过newProxyInstance方法为目标创建代理对象。对目标对象接口进行代理

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

该方法接收三个参数 :

  (1)目标对象类加载器

  (2)目标对象实现的接口

  (3)代理后的处理程序InvocationHandler,是代理实例的调用处理程序 实现的接口

 

方法返回:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

3、 实现InvocationHandler 接口中 invoke方法,在目标对象每个方法调用时,都会执行invoke

 

代码实现:

针对接口进行代理:

//接口(表示代理的目标接口)
public interface ICustomerService {
	//保存
	public void save();
	//查询
	public int find();
}

//实现类
public class CustomerServiceImpl implements ICustomerService{

	public void save() {
		System.out.println("客户保存了。。。。。");
	}

	public int find() {
		System.out.println("客户查询数量了。。。。。");
		return 100;
	}
}

针对接口实现动态代理:

实现jdk动态代理方式1:
public class JdkProxyFactory{
	//成员变量
	private Object target;
	//注入target目标对象
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//参数1:目标对象的类加载器
		//参数2:目标对象实现的接口
		//参数3:回调方法对象
		/**方案一:在内部实现new InvocationHandler(),指定匿名类*/
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler(){

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				//如果是保存的方法,执行记录日志操作
				if(method.getName().equals("save")){
					writeLog();
				}
				//目标对象原来的方法执行
				Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象方法的返回值
				return object;
			}
			
		});
	}
	
	//记录日志
	private static void writeLog(){
		System.out.println("增强代码:写日志了。。。");
	}

}

//实现动态代理的方式2:直接使用调用类作为接口实现类,实现InvocationHandler接口
public class JdkProxyFactory implements InvocationHandler{
	//成员变量
	private Object target;
	//注入target
	public JdkProxyFactory(Object target) {
		this.target = target;
	}
	
	public Object getProxyObject(){
		//参数1:目标对象的类加载器
		//参数2:目标对象实现的接口
		//参数3:回调方法对象
		/**方案三:直接使用调用类作为接口实现类,实现InvocationHandler接口*/
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
	}
	
	//记录日志
	private static void writeLog(){
		System.out.println("增强代码:写日志了。。。");
	}

	//参数1:代理对象
	//参数2:目标的方法对象
	//参数3:目标的方法的参数
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//如果是保存的方法,执行记录日志操作
		if(method.getName().equals("save")){
			writeLog();
		}
		//目标对象原来的方法执行
		Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
		return object;
	}

}

测试:

 //目标:使用动态代理,对原来的方法进行功能增强,而无需更改原来的代码。
	//JDK动态代理:基于接口的(对象的类型,必须实现接口!)
	@Test
	public void testJdkProxy(){
		//target(目标对象)
		ICustomerService target = new CustomerServiceImpl();
		//实例化注入目标对象
		JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(target);
		//获取Proxy Object代理对象:基于目标对象类型的接口的类型的子类型的对象
		ICustomerService proxy = (ICustomerService)jdkProxyFactory.getProxyObject();
		//调用目标对象的方法
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
	}

JDK动态代理的缺点: 只能面向接口代理,不能直接对目标类进行代理 ,如果没有接口,则不能使用JDK代理。

 

CGLIB代理

Cglib代理的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理 !该代理方式需要相应的jar包,但不需要导入。因为Spring core包已经包含cglib ,而且同时包含了cglib 依赖的asm的包(动态字节码的操作类库)

 

1.针对没有接口的类直接代理:

//没有接口的类
public class ProductService {
	public void save() {
		System.out.println("商品保存了。。。。。");
		
	}

	public int find() {
		System.out.println("商品查询数量了。。。。。");
		return 99;
	}
}

2.使用cglib进行代理

public class CglibProxyFactory implements MethodInterceptor {

	private Object target;

	public CglibProxyFactory(Object target) {
		this.target = target;
	}

	public Object getProxyObject() {
		//代理对象生成器
		Enhancer enhancer = new Enhancer();
		
		//设置目标对象
		enhancer.setSuperclass(target.getClass());
		
		//设置回调函数
		enhancer.setCallback(this);	
		
		//创建返回代理对象
		Object object = enhancer.create();
		
		return object;

	}

	
	//回调方法(代理对象的方法)
	//参数1:代理对象
	//参数2:目标对象的方法对象
	//参数3:目标对象的方法的参数的值
	//参数4:代理对象的方法对象
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//如果是保存的方法,执行记录日志操作
		if(method.getName().equals("save")){
			writeLog();
		}
		//目标对象原来的方法执行
		Object object = method.invoke(target, args);//调用目标对象的某个方法,并且返回目标对象
		return object;
	}

// 增强代码
	public static void writeLog() {
		System.out.println("记录日志");
	}

}

测试:

//cglib动态代理:可以基于类(无需实现接口)生成代理对象
	@Test
	public void testCglibProxy(){
		//target目标:
		ProductService target = new ProductService();
		//weave织入,生成proxy代理对象
		//代理工厂对象,注入目标
		CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(target);
		//获取proxy:思考:对象的类型
		//代理对象,其实是目标对象类型的子类型
		ProductService proxy=(ProductService) cglibProxyFactory.getProxyObject();
		//调用代理对象的方法
		proxy.save();
		System.out.println("————————————————————————————————————————");
		proxy.find();
		
	}

总结:

spring在运行期,生成动态代理对象,不需要特殊的编译器.

spring有两种代理方式:

    1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

    2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

使用该方式时需要注意:

   1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,所以spring默认是使用JDK代理。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。

    2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。

3.spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。 面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的结果

 

你可能感兴趣的:(代理)