又到了周二,这代表快周末了,哈哈!但是本帅公司周六补班,你气不气!补就补吧,周日回家哈哈!
快过年了,在这里我先给大家拜个早年,祝大家身体健康、万事如意、来年发大财!
废话不多说,今天咱们来讨论一下设计模式中的代理模式!
首先:什么叫做代理模式,我的理解就是,通过其他人,给自己服务,自己只专注与自己本身的工作就行了!举个例子来说,链家(也就是租房子的),假如你是房东,你就是被代理类,后面称目标类,那么链家就是为你服务的,称之为代理类,你使用代理类的好处就是,你自己只需要专注买房子,收钱就行了,不用再去满大街宣传,每天带人看房子这些额外的工作,说白了,代理类就是给目标类增加一些额外的功能,同时,不改变自己本身的代码! 这么说的已经够明白了吧!
在研究代理类之前,我们先去模拟一下业务代码中的保存!
package com.proxy;
/**
* 模拟业务类接口
* 模拟保存功能
* @author 皇甫
*/
public interface IUserService {
/**
* 保存方法
* @param name
*/
public void save(String name);
}
package com.proxy;
/**
* 业务类的实现类
* @author 皇甫
*/
public class UserServiceImpl implements IUserService {
@Override
public void save(String name) {
System.out.println("-----------"+name+"保存成功------------");
}
}
以上我们模拟了一个保存的功能,现在你的老大有一个需求,需要你去给这个类增加事务属性,此时,最菜的做法是
package com.proxy;
/**
* 业务类的实现类
* @author 皇甫
*/
public class UserServiceImpl implements IUserService {
@Override
public void save(String name) {
System.out.println("开启事务");
System.out.println("-----------"+name+"保存成功------------");
System.out.println("关闭事务");
}
}
这样做你是完成了任务,但是你改变了业务的原有代码,使它不再专注于自己本身的业务,与java提倡的功能单一性理论相悖,而且他也会使业务逻辑变得更加复杂!此时咱们就可以使用设计模式中的代理模式。
代理类再我们日常的使用中被分为 静态代理类、基于jdk的动态代理类、基于cglib的动态代理类;咱们下面先讨论静态代理类,由浅入深,慢慢琢磨!
首先咱们先来看一下静态代理类,它的主要特点是代理类和目标类必须实现同一个接口,在代理类内部加上额外功能即可!至于缺点,咱们先看一下他的实现方式再来谈!
package com.proxy;
/**
*静态代理类 增加事务管理
* @author 皇甫
*/
public class StaticProxy implements IUserService {
private IUserService iUserService;
public StaticProxy(IUserService iUserService) {
this.iUserService = iUserService;
}
@Override
public void save(String name) {
try{
System.out.println("事务开启");
iUserService.save(name);
System.out.println("事务提交");
}catch (Exception e){
System.out.println("事务回滚");
e.printStackTrace();
}finally {
System.out.println("关闭资源");
}
}
}
测试
/**
* 测试静态代理
*/
@Test
public void testStaticProxy(){
IUserService iu = new UserServiceImpl();
StaticProxy staticProxy = new StaticProxy(iu);
staticProxy.save("大皇甫");
}
结果:
事务开启
-----------大皇甫保存成功------------
事务提交
关闭资源
我们重新定义一个业务代理类,让他继承和目标类一样的接口,实现同样的方法,我们就可以这样,既没有改变原有的业务代码,也完成了增加事务的需求;但是不知道大家发现没有这样一个缺点:增加事务的方法不止一个,假如我有200个需要事务的方法,你就需要写200个代理类来完成这个需求,就会产生类爆炸!实际业务开发中,根部不可能!所以我们有没有更好的办法呢?此时就需要引申出另外一个概念:基于jdk的动态代理类
动态代理类必须实现InvocationHandler,覆盖invoke方法!通过new ProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler invocationHandler )返回一个实例,这个实例就是代理对象,使用ClassLoader来动态的生成代理对象,通过反射拿到目标类的构造函数,从而调用目标类的方法,其唯一参数类型是调用处理器接口类型!代码如下
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类
* @author 皇甫
*/
public class DynamicProxy implements InvocationHandler {
private Object target;
/**
* @param target 目标类对象
* @return 代理对象
*
* newProxyInstance
* 参数1 拿到当前类的类加载器
* 参数2 拿到目标对象所实现的接口
* 参数3 InvocationHandler 当用户使用此代理类拦截时,待用本类的invoke方法
*/
public Object newProxyInstance(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 执行拦截 增添额外功能
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = null;
try{
System.out.println("开启事务");
invoke = method.invoke(target, args);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("事务回滚");
e.printStackTrace();
}finally {
System.out.println("关闭资源");
}
return invoke;
}
}
测试:
/**
* 动态代理
*/
@Test
public void testDynamicProxy(){
DynamicProxy d= new DynamicProxy();
IUserService iu = (IUserService)d.newProxyInstance(new UserServiceImpl());
iu.save("大皇甫");
}
结果:
开启事务
-----------大皇甫保存成功------------
提交事务
关闭资源
我们可以很清楚的看到,额外功能依旧被很轻松的追加上去了,日后如果你想给其他类增加额外的功能,只需要更换newProxyInstance(new UserServiceImpl());里面的实体类属性就ok了,但是基于jdk的动态代理只能对实现了接口的类进行代理,那么如何对没有接口的类进行代理呢!请看终极大boss,许多优秀的框架都集成了这个模式-----cglib代理!比如现在如日中天的SpringAOP 他可以代理没有接口的类,但是,他不能代理find修饰的类或者方法,他的本质其实是生成了一个代理类,来继承目标类;而find不能被继承,所以你懂的。。。。
使用这个代理,你需要先导入个jar,这里我给出maven的坐标,有兴趣的同学自行下载
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
cglib代理代码
package com.proxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib代理
* @author 皇甫
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = null;
try{
System.out.println("事务开启");
System.out.println("执行之前方法名字"+method.getName());
invoke = methodProxy.invokeSuper(o, objects);
System.out.println("执行之后的方法名"+method.getName());
System.out.println("事务提交");
}catch (Exception e){
System.out.println("事务回滚");
e.printStackTrace();
}finally {
System.out.println("资源关闭");
}
return invoke;
}
}
测试:
/**
* 测试cglib
*/
@Test
public void testCglibProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new CglibProxy());
UserServiceImpl iu = (UserServiceImpl)enhancer.create();
iu.save("大皇甫");
}
结果:
事务开启
执行之前方法名字save
-----------大皇甫保存成功------------
执行之后的方法名save
事务提交
资源关闭
可以看到,我们依旧完成了对功能的添加,他可以可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口,但是不能代理find方法。