代理模式就是Spring AOP的底层。

代理模式的分类:

  • 静态代理
  • 动态代理

    静态代理

    例子:租房的人,中介,房东三方。而中介就是代理人,通过第三方代理人来租房。
    分析过程:

  • 抽象角色:就是要共同完成的的事,一般使用接口或抽象类解决。

  • 真实角色:被代理的角色。
  • 代理角色:代理真实的角色,代理后,用来添加一些附属的操作。
  • 客户:访问代理的对象的人、

好处:
可以让真实角色更加纯粹,不用管理公共的业务。公共交给大力角色,实现业务分工。公共业务扩展的时候,也方便管理。
缺点:
一个真实角色产生一个代理,代码量多且开发效率低。

就像DAO层实现业务,Service层代理添加业务,Controller层调用。 dao层业务代码量多,无法随便修改,一旦出现问题就全部崩盘,使用代理添加业务逻辑能更好的开发。

代理.png

  1. //dao层
  2. /**
  3. * @author:刘倩云
  4. * @createTime:2021-03-17
  5. */
  6. public class UserDAOImpl implements UserDAO {
  7. public void save() {
  8. System.out.println("添加用户");
  9. }
  10. public void delete() {
  11. System.out.println("删除用户");
  12. }
  13. }
  1. //service层
  2. /**
  3. * @author:刘倩云
  4. * @createTime:2021-03-17
  5. */
  6. public class UserServiceImpl implements UserService {
  7. private UserDAO userDAO;
  8. public void setUserDAO(UserDAO userDAO) {
  9. this.userDAO = userDAO;
  10. }
  11. public void save() {
  12. log("save");
  13. userDAO.save();
  14. }
  15. public void delete() {
  16. log("delete");
  17. userDAO.delete();
  18. }
  19. public void log(String msg){
  20. System.out.println("使用了"+msg+"方法");
  21. }
  22. }

动态代理

动态代理和静态代理的角色一样
动态代理的代理类是自动生成的,不是我们直接写好的。

动态代理分为两类:

  • 基于接口的动态代理 :JDK动态代理
  • 基于类的动态代理:cglib

需要了解两个类:Proxy:代理,生成代理类
InvocationHandler:调用处理程序

写一个类似于工具类的公共类,把公共部分提取出来,直接调用传入需要的参数。
image.png


JDK动态代理

  1. //dao层
  2. /**
  3. * @author:刘倩云
  4. * @createTime:2021-03-17
  5. */
  6. public interface UserDAO {
  7. void save();
  8. void delete();
  9. }
  10. /**
  11. * @author:刘倩云
  12. * @createTime:2021-03-17
  13. */
  14. public class UserDAOImpl implements UserDAO {
  15. public void save() {
  16. System.out.println("添加用户");
  17. }
  18. public void delete() {
  19. System.out.println("删除用户");
  20. }
  21. }

准备一个代理类
步骤:

  • 成员变量为代理目标
  • 有参构造传入代理目标
  • 获取代理对象的方法:

方法中使用 Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {})

  1. /**
  2. * @author:刘倩云
  3. * @createTime:2021-03-18
  4. */
  5. public class JDKProxyFactory {
  6. private Object target; //被代理目标对象
  7. public JDKProxyFactory(Object target){
  8. this.target = target;
  9. }
  10. //返回代理对象
  11. public Object getObject(){
  12. //传入对应的参数
  13. Class clazz = target.getClass();
  14. //使用匿名内部类直接实现InvocationHandler
  15. Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
  16. /**
  17. * @param proxy 代理的实例
  18. * @param method 被增强的方法
  19. * @param args 被增强方法的参数列表
  20. * @return 被增强方法的返回值
  21. * @throws Throwable
  22. */
  23. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  24. System.out.println("被增强的对象:"+target);
  25. System.out.println("方法名:"+method.getName());
  26. System.out.println("----------------------");
  27. //通过反射调用目标方法
  28. Object returnVal = method.invoke(target, args);
  29. System.out.println("-----------------------");
  30. System.out.println("目标增强后");
  31. System.out.println("方法返回值:"+returnVal);
  32. return returnVal;
  33. }
  34. });
  35. return obj;
  36. }
  37. }


测试

@Test
    public void testJDKProxy(){
        UserDAO userDAO = new UserDAOImpl();
        //创建工厂,传入目标对象
        JDKProxyFactory factory = new JDKProxyFactory(userDAO);
        UserDAO object = (UserDAO) factory.getObject();
        object.save();
    }

总结
JDK代理不会代理接口中没有的方法。
tips:JDK的动态代理是按照接口代理的。接口中没有的方法,无法代理。
其实JDK的动态代理就是JDK自己帮我们实现了那个静态代理中的代理类。

cglib代理

添加cglib包

<!-- 添加cglib包-->
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

工厂

/**
 * @author:刘倩云
 * @createTime:2021-03-18
 */
public class CglibProxyFactory {
    //创建目标成员
    private Object target;

    public CglibProxyFactory(Object target){
        this.target = target;
    }

    public Object getObject(){
        //创建一个增强对象
        Enhancer enhancer = new Enhancer();
        //设置父类类型
        enhancer.setSuperclass(target.getClass());
        //设置处理器
        enhancer.setCallback(new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("被增强的对象:"+target);
                System.out.println("方法名:"+method.getName());
                System.out.println("----------------------");
                //通过反射调用目标方法
                Object returnVal = method.invoke(target, args);
                System.out.println("-----------------------");
                System.out.println("目标增强后");
                System.out.println("方法返回值:"+returnVal);
                return returnVal;
            }
        });
        //创建对象并且返回
        return enhancer.create();
    }
}

测试

@Test
public void testCglibProxy(){
    UserDAOImpl userDAO = new UserDAOImpl();
    //创建工厂,传入目标对象
    CglibProxyFactory factory = new CglibProxyFactory(userDAO);
    UserDAOImpl object = (UserDAOImpl) factory.getObject();
    object.update();
}


总结**
CGLib的动态代理是可以代理接口中没有的方法。
主要的原因是,CGLib增强一个方法是使用继承实现的。