面试题:动态代理 未看=!=!=

1.1.1 动态代理回顾

1.1.1.1 动态代理的特点

字节码随用随创建,随用随加载。

它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。

装饰者模式就是静态代理的一种体现。

1.1.1.2 动态代理常用的有两种方式

基于接口的动态代理

提供者:JDK官方的Proxy类。

要求:被代理类最少实现一个接口

基于子类的动态代理

提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar

要求:被代理类不能用final修饰的(最终类)。

1.1.1.3 使用JDK官方的Proxy类创建代理对象

此处我们使用的是一个演员的例子:

在很久以前,演员和剧组都是直接见面联系的。没有中间人环节。

而随着时间的推移,产生了一个新兴职业:经纪人(中间人),这个时候剧组再想找演员就需要通过经纪人来找了。下面我们就用代码演示出来。

/**

 * 一个经纪公司的要求:

 * 能做基本的表演和危险的表演

*/

public interface IActor {

/**

 * 基本演出

 * @param money

 */

public void basicAct(float money);

/**

 * 危险演出

 * @param money

 */

public void dangerAct(float money);

}

 

/**

 * 一个演员

 */

//实现了接口,就表示具有接口中的方法实现。即:符合经纪公司的要求

public class Actor implements IActor{

 

public void basicAct(float money){

System.out.println("拿到钱,开始基本的表演:"+money);

}

 

public void dangerAct(float money){

System.out.println("拿到钱,开始危险的表演:"+money);

}

}

 

public class Client {

 

public static void main(String[] args) {

//一个剧组找演员:

final Actor actor = new Actor();//直接

 

/**

 * 代理:

 * 间接。

 * 获取代理对象:

 * 要求:

 *  被代理类最少实现一个接口

 * 创建的方式

 *   Proxy.newProxyInstance(三个参数)

 * 参数含义:

 * ClassLoader:和被代理对象使用相同的类加载器。

 *  Interfaces:和被代理对象具有相同的行为。实现相同的接口。

 *  InvocationHandler:如何代理。

 *   策略模式:使用场景是:

 *   数据有了,目的明确。

 *   如何达成目标,就是策略。

 *  

 */

IActor proxyActor = (IActor) Proxy.newProxyInstance(

actor.getClass().getClassLoader(),

actor.getClass().getInterfaces(),

new InvocationHandler() {

/**

 * 执行被代理对象的任何方法,都会经过该方法。

 * 此方法有拦截的功能。

 *

 * 参数:

 * proxy:代理对象的引用。不一定每次都用得到

 * method:当前执行的方法对象

 * args:执行方法所需的参数

 * 返回值:

 * 当前执行方法的返回值

 */

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

String name = method.getName();

Float money = (Float) args[0];

Object rtValue = null;

//每个经纪公司对不同演出收费不一样,此处开始判断

if("basicAct".equals(name)){

//基本演出,没有2000不演

if(money > 2000){

//看上去剧组是给了8000,实际到演员手里只有4000

//这就是我们没有修改原来basicAct方法源码,对方法进行了增强

rtValue = method.invoke(actor, money/2);

}

}

if("dangerAct".equals(name)){

//危险演出,没有5000不演

if(money > 5000){

//看上去剧组是给了50000,实际到演员手里只有25000

//这就是我们没有修改原来dangerAct方法源码,对方法进行了增强

rtValue = method.invoke(actor, money/2);

}

}

return rtValue;

}

});

//没有经纪公司的时候,直接找演员。

// actor.basicAct(1000f);

// actor.dangerAct(5000f);

 

//剧组无法直接联系演员,而是由经纪公司找的演员

proxyActor.basicAct(8000f);

proxyActor.dangerAct(50000f);

}

}

1.1.1.4 使用CGLibEnhancer类创建代理对象

还是那个演员的例子,只不过不让他实现接口。

/**

 * 一个演员

*/

public class Actor{//没有实现任何接口

 

public void basicAct(float money){

System.out.println("拿到钱,开始基本的表演:"+money);

}

 

public void dangerAct(float money){

System.out.println("拿到钱,开始危险的表演:"+money);

}

}

 

public class Client {

/**

 * 基于子类的动态代理

 * 要求:

 * 被代理对象不能是最终类

 * 用到的类:

 * Enhancer

 * 用到的方法:

 * create(Class, Callback)

 * 方法的参数:

 * Class:被代理对象的字节码

 * Callback:如何代理

 * @param args

 */

public static void  main(String[] args) {

final Actor actor = new Actor();

 

Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),

new MethodInterceptor() {

/**

 * 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何方法进行增强。

 *

 * 参数:

 * 前三个和基于接口的动态代理是一样的。

 * MethodProxy:当前执行方法的代理对象。

 * 返回值:

 * 当前执行方法的返回值

 */

@Override

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

String name = method.getName();

Float money = (Float) args[0];

Object rtValue = null;

if("basicAct".equals(name)){

//基本演出

if(money > 2000){

rtValue = method.invoke(actor, money/2);

}

}

if("dangerAct".equals(name)){

//危险演出

if(money > 5000){

rtValue = method.invoke(actor, money/2);

}

}

return rtValue;

}

});

cglibActor.basicAct(10000);

cglibActor.dangerAct(100000);

}

}

这个故事(示例)讲完之后,我们从中受到什么启发呢?它到底能应用在哪呢?

1.1.2 解决案例中的问题

思路只有一个:使用动态代理技术创建客户业务层的代理对象,在执行CustomerServiceImpl时,对里面的方法进行增强,加入事务的支持。

/**

 * 用于创建客户业务层对象工厂(当然也可以创建其他业务层对象,只不过我们此处不做那么繁琐)

*/

public class BeanFactory {

 

/**

 * 获取客户业务层对象的代理对象

 * @return

 */

public static ICustomerService getCustomerService() {

//定义客户业务层对象

final ICustomerService customerService = new CustomerServiceImpl();

//生成它的代理对象

ICustomerService proxyCustomerService = (ICustomerService)

Proxy.newProxyInstance(customerService.getClass().getClassLoader()

,customerService.getClass().getInterfaces(),

new InvocationHandler() {

//执行客户业务层任何方法,都会在此处被拦截,我们对那些方法增强,加入事务。

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

String name = method.getName();

Object rtValue = null;

try{

//开启事务

HibernateUtil.beginTransaction();

//执行操作

rtValue = method.invoke(customerService, args);

//提交事务

HibernateUtil.commit();

}catch(Exception e){

//回滚事务

HibernateUtil.rollback();

e.printStackTrace();

}finally{

//释放资源.hibernate在我们事务操作(提交/回滚)之后,已经帮我们关了。

//果他没关,我们在此处关

}

return rtValue;

}

});

return proxyCustomerService;

}

}

posted @ 2018-05-22 20:40  阿善9  阅读(330)  评论(0编辑  收藏  举报