Java核心(四)——动态代理
iwehdio的博客园:https://www.cnblogs.com/iwehdio/
学习自:
1、静态代理
- 代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求。
- 需要在业务代码前后打印日志,如果直接添加的话:
- 直接修改源程序,不符合开闭原则。应该对扩展开放,对修改关闭
- 如果有几十个、上百个方法,修改量太大
- 存在重复代码(都是在核心代码前后打印日志)
- 日志打印硬编码在类中,不利于后期维护
- 静态代理的实现:
- 编写一个代理类,实现与目标对象相同的接口,并在内部维护一个目标对象的引用。
- 通过构造器塞入目标对象,在代理对象中调用目标对象的同名方法,并添加前拦截,后拦截等所需的业务功能。
- 静态代理只解决了上面四个缺点中的第一点。
- 如果需要对多个类进行改造,就需要对每个类都实现一个代理。
- 我们其实想要的并不是代理类,而是代理对象!那么,能否让JVM根据接口自动生成代理对象呢?
2、动态代理
-
思路:通过获得被代理对象的接口的Class对象,获取被代理对象的方法信息。
-
代理Class其实就是附有构造器的接口Class,一样的类结构信息,却能创建实例。
-
java.lang.reflect.Proxy.getProxyClass():返回代理类的Class对象。
-
也就说,只要传入目标类实现的接口的Class对象,getProxyClass()方法即可返回代理Class对象,而不用实际编写代理类。
-
动态代理使用流程:
- 每次调用代理对象的方法都会调用invoke(),且invoke()的返回值就是代理方法的返回值。
-
动态代理底层调用逻辑:
- 静态代理:往代理对象的构造器传入目标对象,然后代理对象调用目标对象的同名方法。
- 动态代理:constructor反射创建代理对象时,需要传入InvocationHandler。
-
引入InvocationHandler的好处是:
- JVM创建代理对象时不必考虑方法实现,只要造一个空壳的代理对象
- 后期代理对象想要什么样的方法实现,我写在invocationHandler对象的invoke()方法里送进来便是
-
invocationHandler的作用,像是把“方法”和“方法体”分离。
-
代理对象中有个成员变量invocationHandler,每一个方法里只有一句话:handler.invoke()。所以调任何一个代理方法,最终都会跑去调用invoke()方法。
-
invoke()方法是代理对象和目标对象的桥梁:
-
但是我们真正想要的结果是:调用代理对象的方法时,去调用目标对象的方法。
-
所以,接下来努力的方向就是:设法在invoke()方法得到目标对象,并调用目标对象的同名方法。
-
invoke()方法的形参:
- Object proxy:很遗憾,是代理对象本身,而不是目标对象(不要调用,会无限递归)
- Method method:本次被调用的代理对象的方法
- Obeject[] args:本次被调用的代理对象的方法参数
-
这样传入目标对象即可:
public class ProxyTest { public static void main(String[] args) throws Throwable { CalculatorImpl target = new CalculatorImpl(); Calculator calculatorProxy = (Calculator) getProxy(target); calculatorProxy.add(1, 2); calculatorProxy.subtract(2, 1); } private static Object getProxy(final Object target) throws Exception { Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(),/*类加载器*/ target.getClass().getInterfaces(),/*让代理对象和目标对象实现相同接口*/ new InvocationHandler(){/*代理对象的方法最终都会被JVM导向它的invoke方法*/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法开始执行..."); Object result = method.invoke(target, args); System.out.println(result); System.out.println(method.getName() + "方法执行结束..."); return result; } } ); return proxy; } }
3、AOP
-
自己写了一个UserController,以及UserServiceImpl implements UserService,并且在UserController中注入Service层对象:
@Autowired private UserService userService;
那么,这个userService一定是我们写UserServiceImpl的实例吗?
- 给UserServiceImpl加了@Transactional 注解,那么注入的就不是我们写UserServiceImpl,而是其的动态代理。
- 要用动态代理完成事务管理,还需要编写一个通知类,并把通知对象传入代理对象,通知负责事务的开启和提交,并在代理对象内部调用目标对象同名方法完成业务功能。
- Spring为了实现事务,也编写了一个通知类,TransactionManager。利用动态代理创建代理对象时,Spring会把transactionManager织入代理对象,然后将代理对象注入。
-
不用AOP时,交叉业务直接写在方法内部的前后,用了AOP交叉业务写在方法调用前后。这与AOP的底层实现方式有关:动态代理其实就是代理对象调用目标对象的同名方法,并在调用前后加增强代码。不过这两种最终运行效果是一样的。
-
AOP的实现:
-
需要一个通知类(TransactionManager)执行事务,一个代理工厂帮助生成代理对象,然后利用动态代理将事务代码织入代理对象的各个方法中。
-
加了个@MyTransactional后,代理工厂给我返回一个代理对象:
-
代理对象方法 = 事务 + 目标对象方法。
-
事务操作,必须使用同一个Connection对象。如何保证?
- 第一次从数据源获取Connection对象并开启事务后,将它存入当前线程的ThreadLocal中,等到了DAO层,还是从ThreadLocal中取。
-
-
AOP事务的实现:
-
ConnectionUtils工具类
public class ConnectionUtils { private ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private static BasicDataSource dataSource = new BasicDataSource(); //静态代码块,设置连接数据库的参数 static{ dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("123456"); } /** * 获取当前线程上的连接 * @return */ public Connection getThreadConnection() { try{ //1.先从ThreadLocal上获取 Connection conn = tl.get(); //2.判断当前线程上是否有连接 if (conn == null) { //3.从数据源中获取一个连接,并且存入ThreadLocal中 conn = dataSource.getConnection(); tl.set(conn); } //4.返回当前线程上的连接 return conn; }catch (Exception e){ throw new RuntimeException(e); } } /** * 把连接和线程解绑 */ public void removeConnection(){ tl.remove(); } }
-
AOP通知(事务管理器)
/** * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接 */ public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * 开启事务 */ public void beginTransaction(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); }catch (Exception e){ e.printStackTrace(); } } /** * 提交事务 */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); }catch (Exception e){ e.printStackTrace(); } } /** * 回滚事务 */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); }catch (Exception e){ e.printStackTrace(); } } /** * 释放连接 */ public void release(){ try { connectionUtils.getThreadConnection().close();//还回连接池中 connectionUtils.removeConnection(); }catch (Exception e){ e.printStackTrace(); } } }
-
自定义注解
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyTransactional { }
-
Service
public interface UserService { void getUser(); } public class UserServiceImpl implements UserService { @Override public void getUser() { System.out.println("service执行..."); } }
-
实例工厂
public class BeanFactory { public Object getBean(String name) throws Exception { //得到目标类的Class对象 Class<?> clazz = Class.forName(name); //得到目标对象 Object bean = clazz.newInstance(); //得到目标类上的@MyTransactional注解 MyTransactional myTransactional = clazz.getAnnotation(MyTransactional.class); //如果打了@MyTransactional注解,返回代理对象,否则返回目标对象 if (null != myTransactional) { ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); TransactionManager txManager = new TransactionManager(); txManager.setConnectionUtils(new ConnectionUtils()); //装配通知和目标对象 proxyFactoryBean.setTxManager(txManager); proxyFactoryBean.setTarget(bean); Object proxyBean = proxyFactoryBean.getProxy(); //返回代理对象 return proxyBean; } //返回目标对象 return bean; } }
-
代理工厂
public class ProxyFactoryBean { //通知 private TransactionManager txManager; //目标对象 private Object target; public void setTxManager(TransactionManager txManager) { this.txManager = txManager; } public void setTarget(Object target) { this.target = target; } //传入目标对象target,为它装配好通知,返回代理对象 public Object getProxy() { Object proxy = Proxy.newProxyInstance( target.getClass().getClassLoader(),/*1.类加载器*/ target.getClass().getInterfaces(), /*2.目标对象实现的接口*/ new InvocationHandler() {/*3.InvocationHandler*/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //1.开启事务 txManager.beginTransaction(); //2.执行操作 Object retVal = method.invoke(target, args); //3.提交事务 txManager.commit(); //4.返回结果 return retVal; } catch (Exception e) { //5.回滚事务 txManager.rollback(); throw new RuntimeException(e); } finally { //6.释放连接 txManager.release(); } } } ); return proxy; } }
-
iwehdio的博客园:https://www.cnblogs.com/iwehdio/