学习概述:学习代理的机制与原理
学习目标:熟练编写某个目标类的代理类对象并且动态调用Java对象的方法
1.代理
<1>程序要为共同接口的目标类增加一些系统功能,例如:异常处理,日志等等,下面的图讲述了代理类与目标类的关系。
<2>使用动态代理后,可以是客户端用户感觉到代理类新提供的系统功能就像在程序中硬编码一样的效果,实际上不是,这样可以实现松耦合,利于程序的维护和扩展。
<3>代理是实现AOP编程的基础
使用动态代理技术
<1>JVM可以在运行期生成字节码,这种动态生成的字节码可以用作Java的代理类。
<2> 目标类必须实现一个或几个接口,因为代理类要执行目标类的方法,要获得目标类的方法信息,所以可以告诉JVM这个类实现了那些借口用来获得方法信息。
<3>如果目标类没有实现接口,那么如何获得该目标类的代理类?可以用第三方类库,CGLIB可以实现这种功能。
<4>代理类除了要实现目标类的方法之外,还要实现一些系统功能,可以在以下四个位置添加功能代码:
目标方法之前,目标方法之后,目标方法前后,在处理异常的Catch块中。
2.关于AOP
系统中存在交叉业务,例如安全,日志,事务等等。AOP就是要让交叉业务模块化,因为我们不可能直接在程序中硬编码,那么我们可以采用AOP的编程方式,达到相同的功能效果。
3.如何获得代理类
Proxy提供用于创建动态代理和代理对象的静态方法,他也是所有代理类的父类。Proxy提供了两个方法来创建动态代理内核动态代理实例
<1> static Class<?> getProxyClass(ClassLoader loader,Class<?>...interfaces)
<2> static Object new ProxyInstance(ClassLoader loader,Class<?>[ ] interfaces,InvocationHandler handler);
import java.lang.reflect.Proxy;
import java.util.Collection;
public class InvocationHandler {
/**
* 获得动态代理类
*/
public static void main(String[] args) {
// 获得代理类
Class<?> c = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//获得实例对象
Collection c1 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), Collection.class.getInterfaces(), (java.lang.reflect.InvocationHandler) new InvocationHandler());
}
}
4.分析InvocationHandler的内部构造
【1】 动态生成的类实现了Collection接口(也可以实现若干接口),生成的代理类有Collection接口中的所有方法和Invocationhandler对象
【2】构造方法接受一个InvocationHandler对象,为什么要接受对象,对象内部的代码干什么用?
【3】InvocationHandler接口中的invoke方法中的三个参数是怎么样的,代表什么含义?
invoke方法中的三个参数分别是代理对象,方法名,参数名。
5.总结分析代理类的运行原理和内部结构
6.怎么样将目标类传递进去?
【1】直接在InvocationHandler实现类中创建目标类的实例对象,可以看出运行效果,但是没有实际意义。
【2】为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的方式
【3】采用匿名内部类的方式,实现类访问外面方法中目标类的实例对象的final引用类型
课后高级练习
1.用工厂方式生成目标类的代理类
package com.lee.ProxyFactory;
/**
*
* @author Administrator
* 定义Dog接口
*
*/
public interface Dog {
public void run();
public void info();
}
package com.lee.ProxyFactory;
/**
*
* @李亮亮
* 定义一个工具类,模拟系统功能,将系统功能方法抽象出来
*
*/
public class DogUtil {
public void method1(){
System.out.println("模拟通用方法");
}
}
package com.lee.ProxyFactory;
public class GunDog implements Dog {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我跑得很快");
}
@Override
public void info() {
// TODO Auto-generated method stub
System.out.println("我是一只猎狗");
}
}
package com.lee.ProxyFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHander implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
DogUtil dogUtil = new DogUtil();
dogUtil.method1();
Object result = method.invoke(target, args);
return result;
}
}
package com.lee.ProxyFactory;
/**
* 实现一个工厂,获得目标类的代理类实例对象
*/
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static Object getProxy(Object obj){
MyInvocationHander myInvocationHander = new MyInvocationHander();
myInvocationHander.setTarget(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), myInvocationHander);
}
}
package com.lee.ProxyFactory;
public class Test {
/**
* 测试Proxy工厂类
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog target = new GunDog();
Dog dog = (Dog) ProxyFactory.getProxy(target);
dog.run();
dog.info();
}
}
总结:其实这个程序已经具有了Spring的一点意思
7. 终极练习:实现类似Spring的可配置AOP框架
【1】实现的关键:创建一个工厂类BeanFactory负责创建目标类或者是代理类的实例对象,并通过配置文件实现切换,附上自己写的一段代码(模拟张老师的思想,但是与张老师又不同);
package com.lee.aop;
/**
* 代理对象工厂
*/
import java.lang.reflect.Proxy;
public class ProxyBeanFactory {
public static Object getProxy(Object target,Advice advice){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.setTarget(target);
//传入系统功能模块
myInvocationHandler.setAdvice(advice);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myInvocationHandler);
}
}
package com.lee.aop;
/**
* 定义一个InvocationHandler
* 其实这一一段代码已经让我们看到了Spring设值注入的影子了
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Advice advice;
private Object target;
public void setAdvice(Advice advice) {
this.advice = advice;
}
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.info();
Object result= method.invoke(target, args);
return result;
}
}
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
*
* @author Administrator
*创建一个bean工厂
*该框架最关键的部分,根据配置文件生成bean或者是代理bean对象
*/
public class BeanFactory {
Properties props = new Properties();
public BeanFactory(InputStream ips){
try{
props.load(ips);
}
catch(IOException e){
e.printStackTrace();
}
}
public Object getBean(String name){
String className = props.getProperty("name");
Object bean =null;
try{
Class<?> clazz = Class.forName(className);
try {
bean = clazz.newInstance();
if(bean instanceof ProxyBeanFactory){
ProxyBeanFactory proxyBeanFactory =(ProxyBeanFactory)bean;
//根据配置生成系统功能类
Advice advice = (Advice) Class.forName(props.getProperty("xxx")).newInstance();
//根据配置文件生成目标对象
Object target =Class.forName(props.getProperty("#xxx")).newInstance();
//生成目标代理对象
Object proxyBean = proxyBeanFactory.getProxy(target, advice);
return proxyBean;
}
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
return bean;
}
}
学习总结:1.学习了动态代理的原理,熟练掌握了开发目标类的代理类的过程,对Spring框架有了进一步的了解
2.这一部分学起来其实很枯燥的,但是认真学习完之后真的是豁然开朗,对java有了更新的认识,也许大家觉得在日常的开发工作中很少主动使用代理,其实在Java EE开发中,代理无处不在,建议这一部分一定好好掌握