public class ArithmeticCalculatorInfo implements ArithmeticCalculator {
}
//下面是没有日志信息等额外需求的接口的实现类;
package com.zhou.dongtaidaili;
public class ArithmeticCalculatorInfo2 implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public void mul(int i, int j) {
int result = i * j;
System.out.println(result);
}
@Override
public void div(int i, int j) {
int result = i / j;
System.out.println(result);
}
}
package com.zhou.dongtaidaili;
public class Person {
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
public Person(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
private void person() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Integer id;
private String name;
}
package com.zhou.dongtaidaili;
import java.util.HashMap;
import java.util.Map;
import javax.management.RuntimeErrorException;
public class PersonServiceInFo implements Service {
/**
* 增加一个人、删除一个人,更新一个人,前提是你要往容器中放这些信息吧?
* 具体做的话,我们可以来写一个Map进行模拟;
*/
private static Map<Integer,Person> persons = new HashMap<Integer,Person>();
//写Map<Integer,Person>类型成员变量的getter方法;
public static Map<Integer, Person> getPersons() {
return persons;
}
/**
* 因为现在我们还没有连接数据库;
* 这样的话,我们在构造器里面提供一个模拟的实现;
*/
public PersonServiceInFo() {
persons.put(1001, new Person(1001,"zhangsan"));
persons.put(1002, new Person(1002,"lisi"));
}
@Override
public void addNew(Person person) {
persons.put(person.getId(), person);
//增加一个人;
}
@Override
public void delete(Integer id) {
//为了看回滚事务,我们做一个判断:
if(id == 1001){
throw new RuntimeException("id为1001的人不能被删除");
}
persons.remove(id);
//删除一个人;
}
@Override
public void update(Person person) {
persons.put(person.getId(),person);
//更新一个人,我把以前那个人给替换掉就更新一个人了;
}
}
package com.zhou.dongtaidaili;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 首先要得到一个PersonServiceInFo被代理类的一个代理类PersonServiceInFoProxy;
* @author Administrator
*
*/
public class PersonServiceInFoProxy {
private PersonServiceInFo target = null;
//声明一个被代理的对象target;
//然后我在创建这个被代理对象的时候把这个target传进来,这里用构造方法传;
public PersonServiceInFoProxy(PersonServiceInFo target){
this.target = target;
}
//然后我有一个方法,返回Service类型的代理对象;
public Service getPersonServiceProxy(){
//声明一个被代理对象;
Service proxy = null;
//然后得到一个代理对象;
proxy = (Service) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//在之前写一句话;
System.out.println("开启事务");
//然后是执行被代理对象的目标方法;
try {
Object result = method.invoke(target, args);
//传的是被代理对象,注意,这个被代理对象是成员变量,所以不能加final;
System.out.println("提交事务");
return result;
} catch (Exception e) {
e.printStackTrace();
//出异常时进行回滚;
System.out.println("回滚事务");
}
return null;
}
});
return proxy;
//这个方法返回一个代理对象;
}
}
package com.zhou.dongtaidaili;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.junit.Test;
public class ProxyTest {
//
@Test
//
public void testCalculator() {
//
ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorInfo();
//
arithmeticCalculator.mul(25, 4);
//
//
}
// 我们来测试一下ProxyTest
@Test
public void testProxy() {
/**
* ---第一步:我需要有一个被代理对象:
* ArithmeticCalculator arithmeticCalculator;
* 这个被代理对象是已经实现接口的类的对象,并且实现那个接口时的方法中没有日志等额外需求的代码,实现接口的方法 * 比较干净;
*/
/**
* public static Object newProxyInstance(ClassLoader loader, Class<?>[]
* interfaces, InvocationHandler h) throws
* IllegalArgumentException的参数说明:
* 第一个参数,ClassLoader:由动态代理产生的对象由哪个类加载器来加载,这个对象比较特殊,
* 正常的对象我们是不是new出来的啊,new的话应该是由系统类加载器进行加载,而这个对象是这个方
* 法凭空生成的,是我们用Proxy.newProxyInstance(null, null, null);这
* 个方法来做的,那么这个时候我们要指定它所使用的类加载器是谁,即这个时候我们需要指定这个对象由 哪一个类 * 加载器来进行加载;
* ---哪一个类加载器呢?通常是与我目标对象使用同一个类加载器,即通常情况下和被代理对象使用一样的类加载器 * ;
*/
final ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorInfo2();
// 这个里面没有其他任何日志代码,所以里面没有参数,这个arithmeticCalculator也就是这个被代理对象;
/**
* ---第二步:获得被代理对象的代理对象:
*/
/**
* 需要代理的情况:
* 第一个参数:我通常和被代理对象(也就是原来没有任何日志的那个对象共用一个的类加载器,即arithmeticCalculator
* .getClass().getClassLoader()---即通过的是对象所在的类,再找到类加载器); 第二个参数Class<?>[]
* interfaces:是Class类型的数组,由动态代理产生对象必须需要实现的那些接口,将其放Class数组里,
* 这里只实现了一个ArithmeticCalculator接口,所以这样写new
* Class[]{ArithmeticCalculator.class,
* 接口其实也是一个抽象类,所以直接用(类.class)就得到其Class类的对象
* ,也就是说我们要产生这个对象的话,我们必须要知道这个代理对象是什么类型的
* ,这个类型必须是放在接口的Class[]数组。--这个说明什么?说明我产生 这个代理对象后,这个代理对象也可以来实现这些接口的方法;
* 第三个参数:当我调用接口的那些方法的时候,它要产生什么行为呢?就是我们这个第三个参数InvocationHandler---
* 当具体调用代理对象的方法时
* ,将产生什么行为(我们已经有类加载器了,也就是第一个参数,比如说经纪人和艺人都是属于同一家公司的;我还代理的是哪一个艺人(也
* 就是这里的第二个参数
* ,放在Class类的数组里面的那些所要实现的接口所在的Class对象),现在是艺人给了你了,她怎么做啊,这个就是第三个参数要干的事情
* ,当我在调用执行被代理对象的一个方法时同时会产生的反应),InvocationHandler是一个接口,我们需要提供这个接口的实现;
* ----------写完之后呢?这个就是一个 proxy,我现在来调proxy的方法;
*
*/
ArithmeticCalculator proxy = (ArithmeticCalculator) Proxy
.newProxyInstance(arithmeticCalculator.getClass()
.getClassLoader(),
new Class[] { ArithmeticCalculator.class },
new InvocationHandler() {
/**
* 目标方法在哪呢?目标方法在public Object invoke(Object proxy,
* Method method, Object[] args)这个里面;
* ---这个里面有三个参数:第一个参数proxy: 第二个参数method:是正在被调用的方法;
* 第三个参数args:调用方法时传入的参数;
*/
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// System.out.println("invoke...");//我先来打印一句话;
// 我打印一个method:
// System.out.println("Method: "+method);
// /**
// * 调用proxy.mul(5, 10)时:Method: public abstract
// void
// com.zhou.dongtaidaili.ArithmeticCalculator.mul(int,int)
// * 调用proxy.add(8, 10)时:Method: public abstract
// int
// com.zhou.dongtaidaili.ArithmeticCalculator.add(int,int)
// */
// System.out.println(Arrays.asList(args));//看看调用方法时传入的参数,这里将数组用List集合的形式列出,看得清楚点;
//
// 在调用这个方法开始执行的时候我来打印一句话:
System.out.println("the method:" + method.getName()
+ ",begin with:" + Arrays.asList(args));
/**
* 到这个时候清楚了:这个时候来调用被代理类的目标方法:
*/
Object result = method.invoke(
arithmeticCalculator, args);
// 第一个参数说明是代理谁的,arithmeticCalculator这个就是那个被代理对象;
/**
* 这个时候会讲被代理对象所在的类变成一个final类:如下所示: final
* ArithmeticCalculator arithmeticCalculator =
* new ArithmeticCalculatorInfo2();//
* 这个里面没有其他任何日志代码
* ,所以里面没有参数,这个arithmeticCalculator也就是这个被代理对象;
*/
// 在调用这个方法执行结束的时候我再来打印一句话:
System.out.println("the method:" + method.getName()
+ ",end with:" + Arrays.asList(args));
return result;
// 现在统一返回一个值0;
}
});
// 我需要的是一个对象,Proxy类有一个方法,这个方法叫newProxyInstance(参数1,参数2,参数3),然后呢,我来产生一个方法,它有三个参数,暂且先写为null;
/**
* 第三步:我写完代理对象之后,我肯定要去调用这个代理对象代理的被代理对象的某些个方法;
*/
// 写完之后呢?这个就是一个 proxy,我现在来调proxy的方法;
proxy.mul(5, 10);
// 在执行的时候在调用这句System.out.println("invoke...");
int result = proxy.add(8, 10);
// 在执行的时候在调这句System.out.println("invoke...");----即都在调用InvocationHandler这个接口的实现;
System.out.println(result);
/**
* ---这个时候程序执行的结果是:
* the method:mul,begin with:[5, 10]
*
50
*
the method:mul,end with:[5, 10]
*
the method:add,begin with:[8, 10]
*
the method:add,end with:[8, 10]
*
18
* 这个时候我们就将这个程序模块化了,使用动态代理,修改起来也很容易;
*/
}
/**
* --小结:首先讲了什么是代理,这里并没有讲代理模式
* 然后讲了一个方法,Proxy类的public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* throws IllegalArgumentException
* 方法,这里面有三个参数,我将通过这个方法,而不使用new的方式生成一个代理对象,那么这个时候,这个代理对象就需要指定由哪个类加载器
* 来进行加载,第二个需要指定,我们这个代理产生的对象需要实现哪些接口,即我这个代理是什么类型的,你可以指定多个接口,但是必须是
* 接口对应的Class类类型的数组,不是接口不行;最后一个InvocationHandler参数,是在我具体调用被代理对象的方法的时候,
* 我都需要干些什么,就需要一个InvocationHandler接口的实现类对象,InvocationHandler是个接口,我们用这个接
* 口创建一个匿名内部类的实现类实例,需要实现InvocationHandler接口的public Object invoke(Object proxy, Method method,
*
Object[] args) throws Throwable;这个方法,第一个参数是通过Proxy类的ewProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)方法获得的被代理对象的类的代理对象;第二个参数是被代理对象实现的比较干净的正在被调用的那个目标方法,第三个参数是正在调用的那个方法需要传入的参数;
* 再在这个InvocationHandler接口的实现类中调用Method对象的 method.invoke(obj, args)方法来执行被代理对象的这个目标方法,这里的obj是被代理对象,args是这个被代理对象的这个方法的参数;
*/
/**
* 关于动态代理的一些细节:
* 1.需要一个被代理的对象;
* 2.一般的,代理对象Proxy.newProxyInstance()的返回值一定是一个被代理对象实现的接口的类型ArithmeticCalculator proxy,当然也可以是其他的接口类型;
* 注意:第二个参数必须是一个接口类型的数组,里面不能有任何的类;
* 提示:如果代理对象不需要实现被代理对象实现的接口以外的接口时,可以使用(被代理对象.getClass().getInterfaces())这样的方法来获取第二个接口数组类型的参数;
* 3.创建InvocationHandler接口的实现类对象,通常使用匿名内部类的方式创建;
* ---注意:被代理对象需要是final类型的:因为有可能这个方法调用结束了,但是这个代理对象还没有变成垃圾,但是我这个代理对象是不是用到了被代理对象?那我这个方法要是结束
* 了的话,我写的这个代理对象是个局部变量,方法结束了局部变量是不是会变成垃圾啊?但是我产生的代理对象有可能在方法结束时不是垃圾吧,这个时候我在调其他方法的时候我
* 需要用到这个被代理对象,就要求这个被代理对象不能是一个局部变量,要求这个在方法执行完之后不能变成垃圾,所以说它需要是final类型;
* 4.InvocationHandler的invok()方法中的第一个参数Object类型的proxy指的是正在被返回的那个代理对象,一般情况下不使用它;
*/
@Test
public void testProxy2(){
//第一步:需要一个被代理的对象;
final ArithmeticCalculator target = new ArithmeticCalculatorInfo2();//被代理对象;
//打印一下被代理对象所在类实现的接口:
System.out.println(Arrays.asList(target.getClass().getInterfaces()));
//[interface com.zhou.dongtaidaili.ArithmeticCalculator]
//第二步:获得代理对象;类加载器通常和被代理对象的类加载器一样;
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
//new Class[]{ArithmeticCalculator.class,Validator.class},
//如果我要跟被代理对象用同样的接口,我可以像下面这样写:通过被代理对象的类来获取所有的被代理对象所在类实现了的所有的接口;
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
obj = method.invoke(target, args);
} catch (Exception e) {
e.printStackTrace();
//当调用方法出异常了,就打印异常信息,叫异常通知;
}
return obj;
}
});
// 这里不写调用被代理方法要做的额外的需求的代码;
int re = ((ArithmeticCalculator) proxy).add(1,2);
//Object proxy,是Object类型的代理对象,使用的时候需要强转;
System.out.println(re);
((ArithmeticCalculator) proxy).mul(2,3);
}
/**
* 练习:
* 定义一个Service接口:
* 定义如下方法:
* addNew(Person person);
* delete(Integer id);
* update(Person person);
*
* 并提供具体的实现类。
*
* 使用动态代理实现事物操作:
* 1.在具体调用每个Service方法前,都打印:开始事物;
* 2.方法正常结束,都打印:事物提交;
* 3.若在调用目标方法出异常的情况下:打印:事物回滚;
*/
@Test
public void testPersonService(){
//在测试之前首先要得到ServiceInFo类的一个代理类
Service target = new PersonServiceInFo();
//创建一个被代理对象;
Service proxy = (Service) new PersonServiceInFoProxy((PersonServiceInFo) target).getPersonServiceProxy();
//下面来操作这个代理对象proxy;
//往里面放一个人;
//我在执行之前先打印这个人,看看效果;
System.out.println(PersonServiceInFo.getPersons());
proxy.addNew(new Person(2007, "周星驰"));
//为了看看效果,我在被代理对象所在的那个PersonServiceInFo类里面改写:
//我在执行之后再打印一下这个人,看看效果;
System.out.println(PersonServiceInFo.getPersons());
/**
* 程序执行结果:
* {1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi]}
*
开启事务
*
提交事务
*
{1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi], 2007=Person [id=2007, name=周星驰]}
*/
//添加方法之后还有一个方法,删除方法:
proxy.delete(1001);
//再打印一遍:
System.out.println(PersonServiceInFo.getPersons());
/**
* 程序执行结果:
* {1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi]}
*
开启事务
*
提交事务
*
{1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi], 2007=Person [id=2007, name=周星驰]}
*
开启事务
*
提交事务
*
{1002=Person [id=1002, name=lisi], 2007=Person [id=2007, name=周星驰]}
*/
//还有一个方法,更新的方法:
proxy.update(new Person(1002, "周润发"));
//更新之后再打印一遍:
System.out.println(PersonServiceInFo.getPersons());
/**
* 程序执行结果:
* {1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi]}
*
开启事务
*
提交事务
*
{1001=Person [id=1001, name=zhangsan], 1002=Person [id=1002, name=lisi], 2007=Person [id=2007, name=周星驰]}
*
开启事务
*
提交事务
*
{1002=Person [id=1002, name=lisi], 2007=Person [id=2007, name=周星驰]}
*
开启事务
*
提交事务
*
{1002=Person [id=1002, name=周润发], 2007=Person [id=2007, name=周星驰]}
*/
}
}