Spring(春天)核心之 aop(重点)
一、spring(春天)核心之 aop(重点)
先了解:oop: Object Orinted programming 面向对象编程 ,是一个思想。
举个最简单点的例子来区分 面向过程和面向对象 有一天你想吃鱼香肉丝了,怎么办呢?你有两个选择 1、自己买材料,肉,鱼香肉丝调料,蒜苔,胡萝卜等等然后切菜切肉,开炒,盛到盘子里。 2、去饭店,张开嘴:老板!来一份鱼香肉丝! 看出来区别了吗?这就是1是面向过程,2是面向对象。 面向对象有什么优势呢?首先你不需要知道鱼香肉丝是怎么做的,降低了耦合性。如果你突然不想吃鱼香肉丝了,想吃洛阳白菜,对于1你可能不太容易了,
还需要重新买菜,买调料什么的。对于2,太容易了,大喊:老板!那个鱼香肉丝换成洛阳白菜吧,提高了可维护性。总的来说就是降低耦合,提高维护性!
面向对象的三大特性:
隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
2、继承
提高代码复用性;继承是多态的前提。
3、多态
父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
重点:aop: Aspect Oriented Programming 面向切面编程 ,是一种思想。
1、什么是aop:
AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,
Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。 在不改变原有的逻辑的基础上,增加一些额外的功能。代理也是这个功能,读写分离也能用aop来做。
AOP可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,
用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。
日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、
异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。
所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。
横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
2、AOP的相关概念:
(1)横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点 (2)Aspect(切面):通常是一个类,里面可以定义切入点和通知 (3)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,
实际上连接点还可以是字段或者构造器 (4)Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕) (5)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式 (6)weave(织入):将切面应用到目标对象并导致代理对象创建的过程 (7)introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段 (8)AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类 (9)目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
3、AOP使用场景:
Authentication 权限 Caching 缓存 Context passing 内容传递 Error handling 错误处理 Lazy loading 懒加载 Debugging 调试 logging, tracing, profiling and monitoring 记录跟踪 优化 校准 Performance optimization 性能优化 Persistence 持久化 Resource pooling 资源池 Synchronization 同步 Transactions 事务
总结: oop:纵向抽取
好处:1)提高可重用性
2)提高可扩展和可维护
3)多态
aop:横向向抽取
好处:1)提高可重用性
2)提高可扩展和可维护
二、aop怎么玩? aop底层原理:动态代理技术 jdk动态代理和cglib动态代理
代理========>经纪人、中介
三、动态代理(重点)
黑客入侵案例
1)jdk黑客: 只能入侵实现接口的对象。 针对接口实现类
jdk接口:InvocationHandler
==>JDK动态代理技术
【重点】jdk本质: 字节拼接技术为接口自动生成了一个代理类$Proxy0(子类)
2)spring黑客:针对类(aspect包---spring提供的)
aopalliance: MethodInteceptor 当类实现接口内部用的jdk黑客 如果类没有实现接口 使用cglib动态代理
==>可以使用JDK动态代理(实现接口类) 也可以使用Cglib动态代理(类或实现接口类)
【重点】Cglib本质: 生成了一个类的代理类
四、aop的术语
连接点 可以被增强的方法(spring 的连接点 oop中的方法)
切点 被增强的方法
通知/增强 增强的逻辑
切面 就是切点和通知(增强)的统称
植入 将通知植入到目标方法的过程
目标对象 被增强的对象
代理对象
aop:将对象的目标方法通过动态代理技术进行植入增强,从而达到
增强方法的目的
补充:xml配置aop太过繁琐,aopalliance 中出现了一种AspectJ技术
1)通过注解配置切点(哪个方法需要被入侵)和通知(黑客)
2)自动生产代理对象
第1步:创建AppConfig.java
@Configuration //标示为配置类 相当于xml文件
//扫描指定包下包含@Component注解的类,将这个类加入spring bean工程
@ComponentScan(value= {"com.cc.aspect","com.cc.dao.impl"})
@EnableAspectJAutoProxy//启动aspectJ注解aop
public class AppConfig {
}
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1、目标对象 --> <bean id="target" class="com.cc.dao.impl.UserDaoImpl2"></bean> <bean id="target2" class="com.cc.dao.impl.UserDaoImpl"></bean> <!-- 2、黑客对象 --> <bean id="springHk" class="com.cc.proxy.SpringHk"></bean> <!--3、代理对象:是目标对象与黑客对象融合体(cglib) --> <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 1)注入目标对象 --> <property name="targetName" value="target"/> <!-- 2)黑客对象 --> <property name="interceptorNames"> <array> <value>springHk</value> </array> </property> </bean> <!--3、代理对象:是目标对象与黑客对象融合体(jdk) --> <bean id="userDaoProxyJdk" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="com.cc.dao.UserDao"></property> <!-- 1)注入目标对象 --> <property name="target" ref="target2"/> <!-- 2)黑客对象 --> <property name="interceptorNames"> <array> <value>springHk</value> </array> </property> </bean> </beans>
第2步:创建@Aspect 切面类
package com.cc.dao; public interface UserDao { int add(int a, int b); }
package com.cc.dao.impl; import com.cc.dao.UserDao; public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a+b; } }
package com.cc.dao.impl; public class UserDaoImpl2 { public int add(int a, int b) { return a+b; } }
package com.cc.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import com.cc.dao.UserDao; /** * @ClassName: JdkHk * @Description: TODO * @author 新梦想IT学院.陈超 * @date 2020年12月16日 证件(标准)------接口:一组功能约定 */ public class JdkHk implements InvocationHandler{ Object target;//目标 public JdkHk(Object target) { this.target = target; } /** * 入侵目标的方法 * proxy:代理对象 * method: 目标对象的方法 * args:调用目标对象方法传递过参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("====亲,你被入侵了==="); //改变传入的参数 args[0]=100; args[1]=2; //调用目标方法 System.out.println("======鉴权成功====="); Object result = method.invoke(target, args); System.out.println("result=="+result); System.out.println("======记录成功======"); return result; } }
package com.cc.proxy; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactoryBean; /* * springHk的证------->spring aop */ public class SpringHk implements MethodInterceptor{ /** * 入侵的方法 * MethodInvocation 目标方法 */ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { //获取目标方法参数 Object[] arguments = methodInvocation.getArguments(); arguments[0]=100; arguments[1]=22; System.out.println("=======spring hk入侵======"); //调用目标方法 Object result= methodInvocation.proceed(); return result; } }
package com.cc.proxy; import java.lang.reflect.Proxy; import com.cc.dao.UserDao; import com.cc.dao.impl.UserDaoImpl; import com.cc.dao.impl.UserDaoImpl2; public class TestOne { public static void main(String[] args) { // 一、正常调用 UserDao userDao = new UserDaoImpl(); int result = userDao.add(3, 5); System.out.println(result); // 二、jdkHk入侵调用 // 1.目标对象 //UserDaoImpl2 target = new UserDaoImpl2(); UserDao target = new UserDaoImpl(); // 2.黑客对象 JdkHk jdkHk = new JdkHk(target); // 3.生产代理对象 /** * ClassLoader类加载器 .java---.class---类加载器(加载字节码) ---JVM的方法区---开辟空间...... * * jdk动态代理底层是使用的字节拼码接技术(ASM框架),本质是动态生成了 * 一个UserDao接口的实现类 $Proxy0类 */ UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[] { UserDao.class }, jdkHk); result = userDaoProxy.add(3, 50); } }
package com.cc.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import com.cc.dao.UserDao; /** * @ClassName: JdkHk * @Description: TODO * @author 新梦想IT学院.陈超 * @date 2020年12月16日 证件(标准)------接口:一组功能约定 */ public class JdkHk implements InvocationHandler{ Object target;//目标 public JdkHk(Object target) { this.target = target; } /** * 入侵目标的方法 * proxy:代理对象 * method: 目标对象的方法 * args:调用目标对象方法传递过参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("====亲,你被入侵了==="); //改变传入的参数 args[0]=100; args[1]=2; //调用目标方法 System.out.println("======鉴权成功====="); Object result = method.invoke(target, args); System.out.println("result=="+result); System.out.println("======记录成功======"); return result; } }
第3步:测试
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:beans.xml") public class SpringHkTest { @Autowired @Qualifier("userDaoProxy") private UserDaoImpl2 userDaoImpl2; @Resource(name="userDaoProxyJdk") private UserDao userDao; @Test public void testSpringHk() { //cglib System.out.println( userDaoImpl2.add(30, 5));; // jdk System.out.println(userDao.add(34,22)); } }