java-动态代理-spring AOP由浅到深
一、理解:
1.动态代理解释:无入侵的代码扩展,在不修改源码的情况下增强方法功能。客户不想或者不能直接引用另一个对象,这时客户端和目标对象中间的媒介就是动态代理。在程序运行时,通过反射机制生成。动态代理事先不知道会代理什么,运行时才知道。简单的举个小例子:把所有类中的方法前后打日志,此时想到的就是动态代理。
2.静态代理解释:在学习动态代理之前,简单理解一下静态代理。静态代理是每个类都需要创建一个代理对象,每个方法都有一个委托。动态代理是用反射动态来反射的方法。下面是静态代理的简单小案例:(建议学完动态代理再来看静态代理代码,更容易看出区别)
package TestProtected; public class StaticProxyTest { //-------------BaseService 接口----------------- static interface BaseService { void say(); } //-------------RealService 实现了 BaseService 接口----------------- static class RealService implements BaseService { @Override public void say() { System.out.println("i am realService!"); } } //-------------代理ProxySerice 实现了 BaseService 接口----------------- static class ProxySerice implements BaseService { private BaseService realService;//属性变量,是实际对象 //构造函数,带属性的构造函数 public ProxySerice(BaseService baseService) { this.realService = baseService; } //实现公共接口 @Override public void say() { System.out.println("say before"); realService.say(); System.out.println("say after"); } } public static void main(String[] args) { // BaseService realService = new RealService(); // BaseService ProxySerice = new ProxySerice(realService); // ProxySerice.say(); new ProxySerice(new RealService()).say(); } }
3.动态代理分两类:JDK基于接口代理;第三方CGLIB代理类库;jdk动态代理比CGLIB速度快,但性能CGLIB更好一些
4.JDK动态代理:
代码解析:1.代理类分为4部分:代理对象实现InvocationHandler接口;代理类中有一个成员属性变量;代理对象在构造函数中初始化;实现公共接口;
2.调用代理类:在调用时使用Proxy类中的newProxyInstance()方法动态创建类。
代码:
1.接口Subject
package TestProtectesOne; public interface Subject { void request(); void hello(); }
2.接口实现类
package TestProtectesOne; public class RealSubject implements Subject{ @Override public void request() { System.out.println("real subject execute request"); } @Override public void hello() { System.out.println("hello"); } }
3.代理类 实现了InvocationHandler接口,并且实现了其中的invoke方法
package TestProtectesOne; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class JdkProxySubject implements InvocationHandler{ //成员变量 private Object realSubject; //构造器 public JdkProxySubject(Subject subject) { this.realSubject = subject; } //问:在代理类里面没有实现类的任何东西出现? 答:对的 /* *invoke方法方法参数解析 *Object proxy:指被代理的对象。 *Method method:要调用的方法 *Object[] args:方法调用时所需要的参数 *InvocationHandler接口的子类可以看成代理的最终操作类。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("befor"); try { //利用反射动态的来反射方法,这就是动态代理和静态代理的区别 /* * Object obj :代理的接口对象 * Object... args :接口对象方法的参数 * */ method.invoke(realSubject, args); } catch (Exception e) { System.out.println("ex:"+e.getMessage()); throw e; } finally { System.out.println("after+++++++"); } //问和返回null有什么区别? return null; } }
4.调用主函数
package TestProtectesOne; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; //调用者 public class Client { /* *newProxyInstance方法参数解析 *ClassLoader loader:类加载器 *Class<?>[] interfaces:得到全部的接口 *InvocationHandler h:得到InvocationHandler接口的子类实例 */ public static void main(String[] args) { //在没有扩展之前,调用继承类中的方法 // Subject subject = new RealSubject(); // subject.hello(); // subject.request(); //现在想在这两个方法之前打日志 //Subject subject1 = new RealSubject(); //new 一个代理类 传入的参数是实体类 InvocationHandler handler = new JdkProxySubject(new RealSubject1()); //Proxy.newProxyInstance(Client.class.getClassLoader(), subject1, handler); Subject subject2 = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, handler); subject2.request(); subject2.hello(); } }
注:其中有一个参数是类加载器,下一个文章是类加载器
jdk动态代理项目总结:jdk首先必须要有一个接口A1(Subject),实现了A1 的实现类 B1(RealSubject)。在之前的项目中直接可以用调用方法的类C1(Client)调用实现类里面的方法。但是现在有一个需求:在B1方法前后打日志。所以引入了动态代理的思想,创建一个代理类(JdkProxySubject),这个代理类指向了接口A1。在调用类C1通过Proxy.newProxyInstance()方式调用。
5.CGLIB第三方动态代理:
在类没有实现接口时可以使用,就像上面的,在B1没有实现A1的情况下,要求对B1的方法前后打日志,在这个时候代理类可以使用这个方法。实现原理是:加载代理的是class文件,修改字节码生成子类。所以,被代理的类不能是final类型的。
代码解析:
引用包cglib-xxx.jar
非Maven项目还需要手动引用包asm-xxx.jar
- 业务类(不需要定义接口)
- cglib代理类(实现接口MethodInterceptor
代码:
1.业务类
package com.wzq.demo02; /** * 业务类 * * 没有实现接口 * * 如果类是final的,则无法生成代理对象,报错 * * 如果方法是final的,代理无效 * * @author Muscleape * */ public class UserServiceImpl { public void addUser() { System.out.println("增加一个用户。。。"); } public void editUser() { System.out.println("编辑一个用户。。。"); } }
2、cglib代理类,需要实现接口MethodInterceptor
package com.wzq.demo02; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class UserServiceCglib implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 设置回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } /** * 实现MethodInterceptor接口中重写的方法 * * 回调方法 */ @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事务开始。。。"); Object result = proxy.invokeSuper(object, args); System.out.println("事务结束。。。"); return result; } }
调用类
package com.wzq.demo02; public class TestCglib { public static void main(String[] args) { UserServiceCglib cglib = new UserServiceCglib(); UserServiceImpl bookFacedImpl = (UserServiceImpl) cglib.getInstance(new UserServiceImpl()); bookFacedImpl.addUser(); } }
这段参考了:https://www.cnblogs.com/muscleape/p/9018308.html
6.spring aop 代理模式
①如果bean 实现接口,默认是jdk 代理模式
如果bean 没有实现接口,默认是CGLIB代理模式
也可以强行使用CGLIB模式,修改配置文件。
②使用场景:日志、监控、事务、权限、异常等
③不管是那种代理模式,都不能用private和final修饰