应用——>代理对象——>目标对象
使用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.