Spring_代理
1.代理模式。
2.静态代理原理及实践。
3.动态代理原理及实践。
4.Spring AOP原理及实战。
静态代理原理及实践
package test.staticProxy; // 接口 public interface IUserDao { void save(); void find(); } //目标对象 class UserDao implements IUserDao{ @Override public void save() { System.out.println("模拟:保存用户!"); } @Override public void find() { System.out.println("模拟:查询用户"); } } /** 静态代理 特点: 1. 目标对象必须要实现接口 2. 代理对象,要实现与目标对象一样的接口 */ class UserDaoProxy implements IUserDao{ // 代理对象,需要维护一个目标对象 private IUserDao target = new UserDao(); @Override public void save() { System.out.println("代理操作: 开启事务..."); target.save(); // 执行目标对象的方法 System.out.println("代理操作:提交事务..."); } @Override public void find() { target.find(); } }
静态代理的缺点:
代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理
如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法,增加了代码的 维护成本,使用动态代理可以解决
动态代理原理及实践
package com.tanlei.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author <a href="mailto:lei.tan@vtradex.net">谭磊</a> * @since 2019-01-02 17:16 */ //动态代理: 代理工厂,给多个目标对象生成代理对象 public class ProxyFactory { //接收一个目标对象 private Object target; public ProxyFactory(Object target){ this.target=target; } //返回目标对象(target)代理后的对象(Proxy) public Object getProxyInstance(){ Object proxy= Proxy.newProxyInstance( target.getClass().getClassLoader(),//目标对象使用的 类加载器 target.getClass().getInterfaces(), //目标对象实现的 所有接口 new InvocationHandler() { //执行代理对象方法时触发 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取当前执行的方法的方法名 String methodName=method.getName(); //方法返回值 Object result=null; if("find".equals(methodName)){ //直接调用目标对象方法 result=method.invoke(target, args); }else{ System.out.println("开启事务..."); //执行目标对象方法 result=method.invoke(target, args); System.out.println("提交事务..."); } return result; } } ); return proxy; } }
package com.tanlei.test; /** * @author <a href="mailto:lei.tan@vtradex.net">谭磊</a> * @since 2019-01-02 17:04 */ public class Main { public static void main(String[] args) { //创建目标对象 IUserDao target=new UserDao(); System.out.println("目标对象: "+target.getClass()); //代理对象 IUserDao proxy= (IUserDao) new ProxyFactory(target).getProxyInstance(); System.out.println("代理对象: "+proxy.getClass()); //执行代理对象的方法 proxy.save(); } }
缺点:
使用jdk生成的动态代理的前提是目标类必须有实现的接口。但这里又引入一个问题,如果某个类没有实现接口,就不能使用JDK动态代理,所以Cglib代理就是解决这个问题的。
spring AOP原理及实战
AOP的定义:面向切面编程,核心原理是使用动态代理模式在方法执行前后或出现异常时加入相关逻辑。
通过定义和前面代码我们可以发现3点:
1.AOP是基于动态代理模式。
2.AOP是方法级别的(要测试的方法不能为static修饰,因为接口中不能存在静态方法,编译就会报错)。
3.AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。
Spring是如何生成代理对象的?:
1.创建容器对象的时候,根据切入点表达式拦截的类,生成代理对象。
2.如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用Cglib代理。然后从容器获取代理后的对象,在运行期植入"切面"类的方法。通过查看Spring源码,我们在DefaultAopProxyFactory类中,找到这样一段话。
知道了原理,现在我们将自己手动实现Spring的AOP:
package test.spring_aop_anno; import org.aspectj.lang.ProceedingJoinPoint; public interface IUserDao { void save(); } //用于测试Cglib动态代理 class OrderDao { public void save() { //int i =1/0;用于测试异常通知 System.out.println("保存订单..."); } } //用于测试jdk动态代理 class UserDao implements IUserDao { public void save() { //int i =1/0;用于测试异常通知 System.out.println("保存用户..."); } } //切面类 class TransactionAop { public void beginTransaction() { System.out.println("[前置通知] 开启事务.."); } public void commit() { System.out.println("[后置通知] 提交事务.."); } public void afterReturing(){ System.out.println("[返回后通知]"); } public void afterThrowing(){ System.out.println("[异常通知]"); } public void arroud(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("[环绕前:]"); pjp.proceed(); // 执行目标方法 System.out.println("[环绕后:]"); } }
Spring的xml配置文件
xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userDao" class="test.spring_aop_anno.UserDao">bean> <bean id="orderDao" class="test.spring_aop_anno.OrderDao">bean> <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop">bean> <aop:config> <aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/> <aop:aspect ref="transactionAop"> <aop:around method="arroud" pointcut-ref="transactionPointcut"/> <aop:before method="beginTransaction" pointcut-ref="transactionPointcut" /> <aop:after method="commit" pointcut-ref="transactionPointcut"/> <aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/> <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/> aop:aspect> aop:config> beans>
package com.tanlei.Aop; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author <a href="mailto:lei.tan@vtradex.net">谭磊</a> * @since 2019-01-03 16:32 */ public class Main { private ApplicationContext context=new ClassPathXmlApplicationContext("applicationcontext_xml.xml"); @Test public void testProxy(){ //SpringIOC容器中获取对象,测试spring中jdk动态代理方式 IUserDao userDao= (IUserDao) context.getBean("userDao"); System.out.println(userDao.getClass()); userDao.save(); } @Test public void testCglib(){ // SpringIOC容器中获取对象,测试spring中Cglib动态代理方式 OrderDao orderDao= (OrderDao) context.getBean("orderDao"); System.out.println(orderDao.getClass()); orderDao.save(); } }
到这里,我们已经全部介绍完Spring AOP,回到开篇的问题,我们拿它做什么?
1.Spring声明式事务管理配置。
2.Controller层的参数校验。
3.使用Spring AOP实现MySQL数据库读写分离案例分析
4.在执行方法前,判断是否具有权限。
5.对部分函数的调用进行日志记录。监控部分重要函数,若抛出指定的异常,可以以短信或邮件方式通知相关人员。
6.信息过滤,页面转发等等功能,