一、静态代理

1.1 什么事静态代理

由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

1.2 案列

接口类:

public interface Subject {
	
	public void play();

}

实现类:

public class RealSubject implements Subject{

	public void play() {
		System.out.println("逗你玩!");
	}

}

代理类:

public class ProxySubject implements Subject{

	private Subject subject;
	
	public ProxySubject(Subject subject) {
		this.subject = subject;
		
	}
	
	public void play() {
		subject.play();
	}

}

测试类:

public class Test {
	
	public static void main(String[] args) {
		
		ProxySubject subject = new ProxySubject(new RealSubject());
		subject.play();
	}

}

静态代理优缺点:

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。

2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

另外,如果要按照上述的方法使用代理模式,那么真实角色(委托类)必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

二、动态代理

2.1 什么是动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

2.2 JDK动态代理

   JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。

案列

接口类和实现类同上静态代理一样

代理类:

public class ProxySubject {

	private static void before() {
		System.out.println("方法执行前 !");
	}

	private static void after() {
		System.out.println("方法执行后");
	}

	public static void main(String[] args) {

		final Subject subject = new RealSubject();
		Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
				subject.getClass().getInterfaces(), new InvocationHandler() {
					
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						before();
						Object object = method.invoke(subject, args);
						after();
						return object;
					}
				});
		proxy.play();
	}

}

 JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

2.3 cglib动态代理

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)

public class BookFacadeImpl {

	public void addBook() {  
        System.out.println("新增图书...");  
    }  
}

代理类:

public class BookFacadeCglib implements MethodInterceptor {
	
	//相当于JDK动态代理中的绑定
    public Object getInstance(Class clazz) {  
       
        Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
        enhancer.setSuperclass(clazz);  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this); 
       // 创建动态代理类对象并返回  
       return enhancer.create(); 
    }

	public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("预处理——————");
		Object obj = proxy.invokeSuper(object, args); // 调用业务类(父类中)的方法
		System.out.println("调用后操作——————");
		return obj;
	}
	
	public static void main(String[] args) {
		BookFacadeCglib cglib = new BookFacadeCglib();
		
		BookFacadeImpl proxy = (BookFacadeImpl) cglib.getInstance(BookFacadeImpl.class);
		proxy.addBook();
		
	}

}

2.4.二者优缺点分析
使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。

Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。

从执行效率上看,Cglib动态代理效率较高。

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐