代理设计模式
java有20多种设计模式,代理模式肯定是非常出名的一种。
代理模式可以理解为不直接访问对象,让代理对象去达到某种目的。
一般是用于对方法的增强,在不动原代码的情况下,对该方法运行前,后,异常加一些处理。
最出名的Sping的AOP(面向切面编程)底层就是动态代理帮实现的。
在平时开发中,使用设计模式可以让我们的代码更加具有可扩展性。
静态代理
这个静态代理非常简单的。理解了上述的意思,我们就可以写个差不多的出来。看代码
package com.zyc.proxydesignpattern.staticproxy;
//这个是登录方法接口
public interface UserService {
String login(String username, String password);
}
package com.zyc.proxydesignpattern.staticproxy;
//这个是登录方法实现类,并实现了 login方法
public class UserServiceImpl implements UserService {
@Override
public String login(String username, String password) {
if("admin".equals(username) && "123".equals(password)){
System.out.println("登录成功");
return "loginSuccess";
}else{
System.out.println("登录失败");
return "error";
}
}
}
package com.zyc.proxydesignpattern.staticproxy;
//这个是代理类 也需要实现UserService接口
public class UserServiceProxy implements UserService {
//定义 被代理的对象
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
//在调用的时候,给该方法的前后添加操作。
@Override
public String login(String username, String password) {
System.out.println("---前置---");
String result = userService.login(username,password);
System.out.println("---后置---");
return result;
}
}
@Test
public void staticTest(){
new UserServiceProxy(userServiceByStatic).login("admin","123");
}
//当调用该方法后运行的结果为
---前置---
登录成功
---后置---
上边的三段代码就是一个简单的静态代理的demo
静态代理的缺点比较明显,这是一个UserService的代理,那再有AService,BService呢?
就需要重新创建代理类,比较麻烦。所以静态代理在实际开发中使用并不多。
动态代理(JDK)
说完静态代理,就说说JDK本身自带的动态代理。
JDK的动态代理是靠反射完成的。直接看代码。
package com.zyc.proxydesignpattern.dynamicproxy.jdk;
//接口
public interface UserService {
String login(String username,String password);
}
package com.zyc.proxydesignpattern.dynamicproxy.jdk;
//实现类,到此为止和静态代理是一模一样的。
public class UserServiceImpl implements UserService{
//JDK的动态代理必须要有接口,这是和cglib最大不同的地方
@Override
public String login(String username, String password) {
if("admin".equals(username) && "123".equals(password)){
System.out.println("登录成功");
return "loginSuccess";
}else{
System.out.println("登录失败");
return "error";
}
}
}
JDK的动态代理最重要的就是这个 InvocationHandler
我们需要实现他的 invoke方法,实际上也就是让目标方法运行
package com.zyc.proxydesignpattern.dynamicproxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler{
//目标方法
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
public Object getProxy(){
//获取该类的代理
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
//通过反射让方法运行。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---前置---");
Object result = method.invoke(target,args);
System.out.println("---后置---");
return result;
}
}
@Test
public void dynamicTest(){
private UserService userService = new UserServiceImpl();
//创建MyInvocationHandler 来获取代理类,从而让代理类运行login方法
UserService proxy = (UserService) new MyInvocationHandler(userService).getProxy();
proxy.login("admin","123");
}
//当调用该方法后运行的结果为
---前置---
登录成功
---后置---
有了动态代理,可以说就比静态代理方便了很多。
你需要代理什么,就传什么值就可以。也可以泛型下就不用强转了。
这只是一个小demo。就不弄那么麻烦了。
一会说为什么被代理类要有接口,
如果不想要接口 ,。其实还有一种动态代理,。
动态代理(cglib)
cglib原理是让目标类生成一个子类,然后让子类去进行方法的增强。
package com.zyc.proxydesignpattern.dynamicproxy.cglib;
//被代理类
public class UserService {
//在用cglib的时候, UserService可以没有接口。
//但是 login 方法不能用final修饰,不能用private修饰 ,因为cglib的代理原理是找该类的子类去继承该方法去实现,
//如果用private 或者 final 修饰 无法继承该方法。
public String login(String username, String password) {
if("admin".equals(username) && "123".equals(password)){
System.out.println("登录成功");
return "loginSuccess";
}else{
System.out.println("登录失败");
return "error";
}
}
}
package com.zyc.proxydesignpattern.dynamicproxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//看好包名 一定要用 cglib下的MethodInterceptor
//这个是cglib代理要实现的接口
public class MyCglibProxy implements MethodInterceptor {
//实现 MethodInterceptor 中的 intercept 接口
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---前置---");
//实际方法运行的地方
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("---后置---");
return result;
}
}
package com.zyc.proxydesignpattern.dynamicproxy.cglib;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Method;
//代理工厂
public class ProxyFactory {
/**
* 防止在外边创建
*/
private ProxyFactory(){}
//代理工厂方法。
//这几天有时间的话 在写个工厂设计模式
public static UserService getUserServiceProxy(MyCglibProxy myCglibProxy){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService.class);
enhancer.setCallback(new MyCglibProxy());
//这里就不详细写了,cglib功能挺多的
//setCallbacks可是设置多个代理,然后根据 setCallbackFilter 的 accept 方法 看哪个方法走哪个代理。
// enhancer.setCallbacks(new Callback[]{new MyCglibProxy()});
// enhancer.setCallbackFilter((Method method)-> {
//
// return 0;
// });
return (com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService)enhancer.create();
}
}
@Test
public void cglibTest(){
/*这样搞不方便,可以搞一个工厂
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService.class);
enhancer.setCallback(new MyCglibProxy());
com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService userService = (com.zyc.proxydesignpattern.dynamicproxy.cglib.UserService)enhancer.create();
userService.login("admin","4554");*/
//如果要是不用代理工厂的话,
//按照上边写就行,但是这样写更简洁一点。也更有拓展性
//通过代理工厂获取 代理,然后用代理去运行login
ProxyFactory.getUserServiceProxy(new MyCglibProxy()).login("123","3443");
}
//当调用该方法后运行的结果为
---前置---
登录失败
---后置---
虽然大家都知道反射的效率是很低的。但是在1.8以后 JDK的动态代理还是要比cglib要效率高一点的。
然后我们看看JDK的动态代理和cglib的动态代理有什么不同吧。
借图:
参考:
深入理解CGLIB动态代理机制
动态代理在实际开发中的使用
在这里我们基于 JDK的动态代理来搞
比如,我们开发中获取一个数据需要有降级处理,先从redis中获取,如果没有获取到在从mysql中获取。
我们看代码。
package com.zyc.proxydesignpattern.inaction.service.impl;
import com.zyc.proxydesignpattern.inaction.entity.Project;
import com.zyc.proxydesignpattern.inaction.service.ProjectService;
import org.springframework.stereotype.Service;
//很普通的service层
@Service
public class ProjectServiceImpl implements ProjectService{
public Project getProjectById(String id){
return new Project("123","假装从数据库中取出的项目");
}
}
package com.zyc.proxydesignpattern.inaction.util;
import com.zyc.proxydesignpattern.inaction.entity.Project;
import org.springframework.stereotype.Component;
//同样很普通的redis操作工具
//我们假设id为123的项目在缓存中存在
@Component
public class RedisUtil {
public Project getProjectById(String id){
if(id!=null && "123".equals(id)){
return new Project(id,"假装从缓存中取出的项目");
}else{
return null;
}
}
}
package com.zyc.proxydesignpattern.inaction.util.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//也很普通的代理工具 ,就多了几个操作类
public class DataBaseProxyHandler<T> implements InvocationHandler {
//被代理对象
private Object delegate;
//需要去做事情的接口
private ProxyInterface myProxyInterface;
//需要去做事情的类
private Object param;
//构造器 在这里给赋值
public DataBaseProxyHandler(Object v) {
this.param = v;
}
//proxy方法,返回
public <T> T proxy(T delegate, ProxyInterface myProxyInterface) {
this.myProxyInterface = myProxyInterface;
this.delegate = delegate;
return (T) Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(),
this.delegate.getClass().getInterfaces(), this);
}
//就多了个几个类,其他一模一样
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
if (args != null && args.length > 0){
obj = myProxyInterface.doBegin(param,args);
}else{
obj = myProxyInterface.doBegin(param,null);
}
if (obj != null)
return obj;
obj = method.invoke(this.delegate, args);
if (args != null && args.length > 0){
myProxyInterface.doEnd(obj, param,args);
}else{
myProxyInterface.doEnd(obj, param,null);
}
return obj;
}
}
package com.zyc.proxydesignpattern.inaction.util.proxy;
public interface ProxyInterface<T,V> {
/**
* T 参数代表需要操作对象的工具类
* V 参数实体对象
* @param
* @return
*/
Object doBegin(T t, Object[] param);
/**
* T 参数代表需要操作对象的工具类
* returnObj invok 后返回的参数
*
* @param returnObj
* @param t
* @return
*/
Object doEnd(V returnObj, T t,Object[] param);
}
package com.zyc.proxydesignpattern.inaction.controller;
import com.zyc.proxydesignpattern.inaction.entity.Project;
import com.zyc.proxydesignpattern.inaction.service.ProjectService;
import com.zyc.proxydesignpattern.inaction.util.RedisUtil;
import com.zyc.proxydesignpattern.inaction.util.proxy.DataBaseProxyHandler;
import com.zyc.proxydesignpattern.inaction.util.proxy.ProxyInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class ProjectController {
@Autowired
ProjectService projectService;
@Autowired
RedisUtil redisUtil;
//如果前边动态代理搞明白了。这些代码很容易理解
//就多了个ProxyInterface去进行前置和后置
public Project getProjectById(String id){
//在这里用了个匿名内部类,就不在外边创建新的Proxy了。
//在这里可以更加直观的看,
return new DataBaseProxyHandler<ProjectService>(redisUtil).proxy(projectService, new ProxyInterface() {
//在doBegin方法中是 getProjectById 前做的事情。
//从 DataBaseProxyHandler 的invoke 方法,可以看到 如果返回的是null才会去运行 getProjectById
//如果不为null 则直接返回了。
@Override
public Object doBegin(Object o, Object[] param) {
RedisUtil redisUtil = (RedisUtil)o;
return redisUtil.getProjectById(id);
}
//在doEnd方法中 是 getProjectById 后做的事情
//在 getProjectById 中 doEnd是不用做任何事的。
//但是 如果是 saveProject 呢? 我们可以在doBegin中不做任何事情,
//在doEnd中可以 判断如果saveProject插入到 mysql/oracle 那么在doEnd可以插入到缓存中。
@Override
public Object doEnd(Object returnObj, Object o, Object[] param) {
return null;
}
}).getProjectById(id);
}
}
以上就是动态代理在实际开发中的使用。
github:https://github.com/zycisbg/ProxyDesignPattern