使用JDK中的Proxy技术、CGLIB实现AOP功能

应用——>代理对象——>目标对象
使用Proxy创建代理对象时,目标对象必须实现某个接口,不然会报错!
当目标对象没有实现接口时,可以使用Spring中的/lib/cglib/cglib-nodep.jar来创建代理对象!

一、使用JDK中的Proxy技术实现AOP功能

1。创建一个接口PersonService以及该接口的实现类PersonServiceBean ,PersonService的 代码如下:

package cn.reiyen.aop;

public interface PersonService {
	public void save(String name);
	public void update(String name, int id);
}

PersonServiceBean的 代码如下:

package cn.reiyen.aop;

public class PersonServiceBean  implements PersonService{
	private String user = null;

	public PersonServiceBean() {
	}

	public PersonServiceBean(String user) {
		this.user = user;
	}

	public String getUser() {
		return user;
	}

	public void save(String name) {
		System.out.println("我是save()方法" + name);
	}

	public void update(String name, int id) {
		System.out.println("我是update()方法!");
	}
}

需求如下:如果实现类的user!=null,才可以调用save与update方法,为null则不能调用。当然要解决此问题,可以在save与update方法内部进行判断,但是如果在方法内部进行判断,代码则失去了灵活性,如果以后的需求改变,比如变成user.equals时,则又要在update/save方法中重新进行一次判断。如果save/update这样的方法很多,这样就会很麻烦。其实要解决此问题,可以通过动态代理技术实现。这里的需求其实就是根据要求来拦截一些业务方法,这种编程问题称之为横切性关注点。

2。动态代理实现[JDK]

建立代理工厂JDKProxyFactory ,代码如下:

package cn.reiyen.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxyFactory implements InvocationHandler {
	private Object targetObject;
	public Object createProxyInstance(Object targetObject){
		this.targetObject = targetObject;
		//创建一个代理对象,此对象是一个与目标对象实现了相同接口的对象。第一个参数为目标对象的类装载器,第二个参数为目标对象所实现的所有接口,第三个参数为实现了InvocationHandler接口的对象实例,在这里即为JDKProxyFactory自身
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), 
				this.targetObject.getClass().getInterfaces(), this);
	}
//第一个参数为代理对象,第二个参数被拦截到的方法,第三个参数为拦截到的方法的输入参数;代理对象最后又将拦截到的方法的处理委配回给目标对象自己处理
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {//环绕通知
		PersonServiceBean bean = (PersonServiceBean)this.targetObject;
		Object result = null;
		if(bean.getUser() != null){
		//….advice()-->前置通知	
               try{
                  result = method.invoke(bean, args);
                 //….afteradvice()-->后置通知
               }catch(RuntimeException e){
                      //….exceptionadvice()-->例外通知
             }finally{
                     //….finallyadvice()-->最终通知
             }
		}
		return result;
	}
}

 
简析动态代理:此类根据传递的targetObject对象来生成此目标对象的代理对象,需要强调的是动态代理技术所要代理的对象必须实现一个接口。newProxyInstance参数说明:第一个参数是目标对象的类装载器,第二个参数是目标对象的接口,第三个参数是回调对象,生成的代理对象要执行方法时就是依靠这个回调对象的invoke方法来进行目标对象的方法回调。关于动态代理的其它细节不在此讨论。

 

3.建立junit测试代码,内容如下:

public class AOPTest1 {

@Test  //用户名为空,不执行save方法
public void proxyTest1(){
		JDKProxyFactory factory = new JDKProxyFactory();
		PersonService service = (PersonService) factory.createProxyInstance(new PersonServiceBean());
		service.save("888");
	}
@Test  //用户名为不为空,才执行save方法,
public void proxyTest2(){
		JDKProxyFactory factory = new JDKProxyFactory();
		PersonService service = (PersonService) factory.createProxyInstance(new PersonServiceBean("xxlong"));
		service.save("888");
	}
}

 测试结果:测试1能通过,但不会执行save()方法,所以不会打印出信息提示语句。

测试1能通过,且会执行save()方法,所以打印出信息提示语句:我是save()方法888

在写测试代码时要注意,创建的代理对象只要转化为接口PersonService,而不能转化为PersonServiceBean.因为newProxyInstance方法返回的是一个指定接口的代理类实例,如果测试代码写成如下:

PersonServiceBean service = (PersonServiceBean) factory.createProxyInstance(new PersonServiceBean("xxx"));

 则会出错,出借信息如下:java.lang.ClassCastException: $Proxy5 cannot be cast to cn.reiyen.aop.PersonServiceBean。

注:newProxyInstance方法详细说明:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
     Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });
 
Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

 

二、使用CGLIB实现AOP功能

JDK的Proxy实现代理要求被代理的目标对象必须实现一个接口,而如果目标对象没有实现接口则不能使用Proxy来代理。这时可以借助spring中的cglib jar包来实现代理。CGLIB可以生成目标类的子类,并重写父类的非final修饰符的方法.操作步骤如下:

步骤一、建立PersonServiceBean2类,它与PersonServiceBean的唯一区别就是没有实现任何接口。
步骤二、导入cglib.jar包,创建cglib代理工厂,代码如下:

package cn.reiyen.aop;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibFactory implements MethodInterceptor {
	
	private Object targetObject; //代理的目标对象
	
	public Object createProxyInstance(Object tarObject){
		this.targetObject = tarObject;
		Enhancer enhancer = new Enhancer(); //该类用于生成代理器类
		
		//将目标类指定为代理类的父类,产生的代理类是此目标类的一个子类,
		//产生的这个子类会覆盖此目标类中的所有的非final修饰的方法
		enhancer.setSuperclass(this.targetObject.getClass());
		enhancer.setCallback(this); //设置回调对象为本身
		return enhancer.create();
	}

	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		PersonServiceBean2 bean = (PersonServiceBean2) this.targetObject;
		Object result = null;
		if(bean.getUser() != null){
			result = methodProxy.invoke(targetObject, args);
		}
		return result;
	}
}

 步骤三、建立测试代码:

	@Test 
	public void cglibProxyTest(){
		CGLibFactory factory = new CGLibFactory();
		PersonServiceBean2 service = (PersonServiceBean2) factory.createProxyInstance(new PersonServiceBean2("xxlong"));
		service.save("999");
	}

 这时因为CGLIB生成的目标类PersonServiceBean2的子类,所以应该把创建的代理对象转化成PersonServiceBean2.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值