代理模式和SpringAOP

1. 什么是代理模式?

代理模式的核心作用就是通过代理,控制对对象的访问。它的设计思路是:定义一个抽象角色,让代理角色和真实角色分别去实现它

  • 抽象角色(Subject):通常是一个接口,是一个最普通的业务类型定义,无特殊定义
  • 真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。它只关注真正的业务逻辑
  • 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并在前后可以附加自己的操作

代理模式在Java中分为静态代理和动态代理

2. 静态代理

静态代理分为普通的代理和强制的代理

2.1 普通代理

类图

image-20210228005143046

// Subject.java
public interface Subject {
	public void request();
}

// RealSubject.java
public class RealSubject implements Subject{
	@Override
	public void request() {
		System.out.println("do real subject method");
	}
}

// Proxy.java
public class Proxy implements Subject{

	// 要代理哪个实现类
	private Subject subject;

    /*
    *通过构造方法传进来真实角色进行代理
    */
	public Proxy(Subject subject) {
		this.subject = subject;
	}

	@Override
	public void request() {
		doBefore();
		this.subject.request();
		doAfter();
	}

	private void doBefore() {
		System.out.println("do before");
	}

	private void doAfter() {
		System.out.println("do after");
	}
}

// Test.java
public class Test {
	public static void main(String[] args) {
		Subject realSubject = new RealSubject();
		Subject proxy = new Proxy(realSubject);
		proxy.request();
	}
}

2.2 强制代理

image-20210228012322133

// Subject.java
public interface Subject {
	public void request();

	public Subject getProxy();
}

// RealSubject.java
public class RealSubject implements Subject{
    
	private Subject proxy = null;

	@Override
	public Subject getProxy() {
		this.proxy = new Proxy(this);
		return this.proxy;
	}

	@Override
	public void request() {
		if (isProxy()) {
			System.out.println("do real subject method");
		} else {
			System.out.println("please use proxy");
		}

	}

	// 校验是否是代理访问
	private boolean isProxy() {
		return this.proxy != null;
	}
}

// Proxy.java
public class Proxy implements Subject{

	// 要代理哪个实现类
	private Subject subject = null;

	public Proxy(Subject subject) {
		this.subject = subject;
	}

	@Override
	public void request() {
		doBefore();
		this.subject.request();
		doAfter();
	}

	@Override
	public Subject getProxy() {
		return this;
	}

	private void doBefore() {
		System.out.println("do before");
	}

	private void doAfter() {
		System.out.println("do after");
	}
}

// Test.java
public class Test {
	public static void main(String[] args) {
		Subject realSubject = new RealSubject();
		Subject proxy = realSubject.getProxy();
		proxy.request();
	}
}

相比于普通的代理,强制代理强制了用户必须从真实角色找到代理角色,不允许直接访问真实角色。但是强制代理给真实角色的每个public方法增加了isProxy方法判断是否设置了代理角色,使代码比较冗余,实用性不强

2.3 静态代理的缺点

静态代理因为需要实现抽象接口,因此通常只能代理一个已知的实体类;对于不同业务,如果用静态代理来处理,会产生多个不同的代理角色类,造成业务上的麻烦,那有没有一种代理类,可以代理多个不同的真实角色类呢?有的,动态代理可以做到

3. 动态代理

动态代理比静态代理使用更加广泛,动态代理本质上,代理类不需要我们来管,我们完全可以交给工具去生成代理类即可。Java中的动态代理分为:JDK动态代理和**CGLIB动态代理**

3.1 JDK动态代理

image-20210228025248979

既然动态代理不需要我们去创建代理类,那我们只需要编写一个动态处理器(即真实角色)就可以了。真正的代理角色实例由 JDK在运行时为我们动态的来创建。核心是反射

// Subject.java和RealSubject.java,内容和静态代理的普通代理一样

// JdkProxyHandler.java
public class JdkProxyHandler<T> implements InvocationHandler{

	// 接收真实对象
	private final T subject;

	public JdkProxyHandler(T subject) {
		this.subject = subject;
	}

	private void doBefore() {
		System.out.println("do before");
	}

	private void doAfter() {
		System.out.println("do after");
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		doBefore();
		Object obj = method.invoke(this.subject, args);
		doAfter();
		return obj;
	}

	@SuppressWarnings("all")
	public <T> T getProxy() {
		return (T)Proxy.newProxyInstance(
				subject.getClass().getClassLoader(),
				subject.getClass().getInterfaces(),
				this);
	}
}

//Test.java
public class Test {
	public static void main(String[] args) {
		Subject subject = new RealSubject();
        // 创建一个代理对象实例
		Subject proxy = new JdkProxyHandler<>(subject).getProxy();
		proxy.request();
	}
}

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)是JDK动态代理的核心方法,该方法接收三个参数:

  • 第一个参数指定当前目标对象使用的类加载器,获取加载器的方法是固定的;
  • 第二个参数指定目标对象实现的接口的类型;
  • 第三个参数指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。

JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。但是 JDK 动态代理有个缺憾,或者说特点:JDK 实现动态代理需要实现类通过接口定义业务方法。

源码分析

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
       	// ...

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // ...
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch {
            //....
        }
    }

重点看这四处位置

final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});

这里可以猜测出返回的cons.newInstance(new Object[]{h})是相当于静态代理的代理角色类,具体看引用的文章代理模式的使用总结

3.2 CGLIB动态代理

CGLIB需要导入asm

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>
// CglibProxyHandler.java
public class CglibProxyHandler implements MethodInterceptor {

	private Object subject;

	public CglibProxyHandler(Object subject) {
		this.subject = subject;
	}


	public Object getProxyInstance() {
		// Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
		Enhancer enhancer = new Enhancer();
		// 将被代理的对象设置成父类
		enhancer.setSuperclass(this.subject.getClass());
		// 回调方法,设置拦截器
		enhancer.setCallback(this);
		// 动态创建一个代理类
		return enhancer.create();

	}


	@Override
	public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		doBefore();
		Object result = method.invoke(subject, args);
		doAfter();
		return result;
	}

	private void doBefore() {
		System.out.println("do before");
	}

	private void doAfter() {
		System.out.println("do after");
	}
}

// Test.java
public class Test {
	public static void main(String[] args) {
        Subject subject = new RealSubject();
		Subject proxy = (Subject) new CglibProxyHandler(subject).getProxyInstance();
		proxy.request();
	}
}

代理模式中的动态代理,其实套路都是相同的,只是使用了不同的技术而已。

3.3 总结

CGLIB 创建的动态代理对象比 JDK 创建的动态代理对象的性能更高,但是 CGLIB 创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。同时由于 CGLIB 由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

4. Spring AOP

面向切面编程(Aspect Oriented Programming)提供了另一种角度来思考程序的结构,通过这种方式弥补面向对象编程(Object Oriented Programming)的不足。除了类以外,AOP提供了切面,切面对关注点进行模块化,例如横切多个类型和对象的事务管理(这些关注点术语通常称作横切(crosscutting)关注点)。Spring AOP是Spring的一个重要组件,但是Spring IOC并不依赖于Spring AOP,这意味着你可以自由选择是否使用AOP,AOP提供了强大的中间件解决方案,这使得Spring IOC更加完善。我们可以通过AOP来实现日志监听,事务管理,权限控制等等。

  • Sping AOP采用了哪种代理?

    如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;

    如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。

引用

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页