Spring框架核心细讲 AOP部分
文章目录
前言
此次是对于自己所学Spring中的一些重要知识的回顾与知新, 从浅入深,从使用Spring的好处到核心底层
一、什么是AOP
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
- 如果在登录时,输入了用户名和密码错误,就会退回到l登录页面,
- 如果成功,肯定也不可能直接说直接到功能页面,肯定是需要有个权限模块的方法进行判断,根据权限大小选择不同的主页面呈现
- 此时的权限模块肯定不能以硬编码的形式呈现在代码里,那AOP就没有意义了此时,如果别的地方也要用到这个模块,则无法复用,而且会导致代码里会有很多冗余重复的代码。
- 这就是AOP以及代理出现的原因,实现解耦以及重复代码抽取,以及复用
二.AOP(底层原理)
AOP底层使用了动态代理的方式来实现
2.1 JKD代理
我觉得直接上代码干脆
接口
// 接口
interface MyProxy {
void eat(String food);
void drink(String water);
}
被代理类
class MyClass1 implements MyProxy {
@Override
public void eat(String food) {
System.out.println("吃 " + food);
}
@Override
public void drink(String water) {
System.out.println("喝 " + water);
}
}
实现动态代理需要实现的接口,用来执行对应的方法
class Handler implements InvocationHandler {
private Object obj;
public Handler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodUtils methodUtils = new MethodUtils();
methodUtils.method1();
Object invoke = method.invoke(obj, args);
methodUtils.method2();
return invoke;
}
}
测试类
public class ProxyTest {
public static void main(String[] args) {
Class[] interfaces = {MyProxy.class};
MyClass1 myClass1 = new MyClass1();
Handler handler = new Handler(myClass1);
MyProxy my = (MyProxy) Proxy.newProxyInstance(MyClass1.class.getClassLoader(), interfaces, handler);
my.eat("水果");
my.drink("饮料");
}
}
如果你看了代码还是一头雾水,那我只能说是正常的,这里算是有点比较难理解的
可以看下这篇,将的还是很详细的
对于这篇文章中获取代理对象的流程 代理类生成的是Class<?> 学过反射的应该都知道这是所有类型类的父类
这是文章中对于几个常见问题的解释
2.1.1 为什么需要创建一个接口来实现
代理类继承了Proxy类,其主要目的是为了传递InvocationHandler。这里补充一点,因为代理类已经继承了Proxy,java是不可以多继承的。
2.1.2 为什么代理类可以直接强转成接口
代理类实现了被代理的接口Foo,这也是为什么代理类可以直接强转成接口的原因。
2.1.3 为什么调用代理类中的方法时,总会分派到InvocationHandler中的invoke方法
有一个公开的构造函数,参数为指定的InvocationHandler,并将参数传递到父类Proxy中。
每一个实现的方法,都会调用InvocationHandler中的invoke方法,并将代理类本身、Method实例、入参三个参数进行传递。这也是为什么调用代理类中的方法时,总会分派到InvocationHandler中的invoke方法的原因。
2.2 CGLIB代理
这个代理与JDK代理不一样的是,jdk代理必须有接口实现才行,而且被代理类中需要加强的方法必须在类中声明才行,否则代理失败,
而该代理,则是创建子类的代理对象,增强类的方法
本文不多赘述
感兴趣可以看此篇
三、AOP术语
这里也是大家需要知道的,方便后续的理解
3.1 连接点
类中需要增强的方法 —> 初步决定需要的
3.2 切入点
实际被真正增强的方法 —> 最后决定增强的
3.3 通知(增强)
实际增强的逻辑部分 —> 增强的实现
3.4 通知
前置通知:before 增强方法执行前
后置通知:AfterReturning 增强方法执行返回值后执行
环绕通知:around 方法执行前后
异常通知:afterThrow 方法执行出现异常
最终通知:after 方法执行后通知
注意点:
增强方法除了异常 除了afterReturning不执行 都执行
around环绕通知需要添加 ProceedingJoinPoint proceedingJoinPoint形参 并执行增强方法 proceedingJoinPoint.proceed(); ------> 不执行 before和增强方法都不会有执行结果
3.4 切入点表达式
提取公共切入点表达式
切入点表达式
- 语法 execution(访问修饰符 返回值类型 类全路径.需要增强的方法(…)) ----> 可以使用通配符 * …代表形参
- 作用:知道具体对哪个类的哪个方法进行增强
// 相同切入点抽取
@Pointcut(value = "execution(* com.worker.aopanno.User.add(..))")
public void pointdemo(){
}
3.5 切面
把通知应用到切入点的过程
四、SpringAop实现
注意:
- 需要在增强类上面添加@Aspect注解 生成代理对象
- 多个增强类对同一个方法增强 可以使用@Order(n) 设置增强类优先级 n越小优先级越高
4.0 需要增强的类
@Component
public class User {
public void add() {
System.out.println("add ......");
}
}
4.1 半注解+半xml
代码实现(注解实现):
4.2 xml配置文件
<!--引入aop context命名空间--> 只要有xml配置都需要配置上
<!--这几行就是引入aop context命名空间-->
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"
<!--开启注解扫描-->
<context:component-scan base-package="com.worker"></context:component-scan>
<!--开启Aspectj生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
java代码
// 相同切入点抽取
@Pointcut(value = "execution(* com.worker.aopanno.User.add(..))")
public void pointdemo(){
}
// 前置通知 增强类作为通知方法上添加通知类型
@Before(value = "pointdemo()")
public void before(){
System.out.println("before ......");
}
// 最终通知 方法执行后通知
@After(value = "pointdemo()")
public void after(){
System.out.println("after ......");
}
// 后置通知/返回通知 方法执行返回值后执行 出了异常不执行 其它通知都执行
@AfterReturning(value = "pointdemo())")
public void afterReturning(){
System.out.println("AfterReturning ......");
}
// 增强方法出了异常执行
@AfterThrowing(value = "pointdemo()")
public void afterThrowing(){
System.out.println("AfterThrowing ......");
}
@Around(value = "pointdemo()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("around之前 ......");
// 被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("around之后 ......");
}
4.3 完全xml配置文件实现
<!--创建对象-->
<bean id="person" class="com.worker.aopxml.Person"></bean>
<bean id="personProxy" class="com.worker.aopxml.PersonProxy"></bean>
<!--配置文件aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.worker.aopxml.Person.add(..))"/>
<!--切入面-->
<aop:aspect ref="personProxy">
<!--增强作用在具体方法上-->
<aop:before method="before" pointcut-ref="p"></aop:before>
<aop:after method="after" pointcut-ref="p"></aop:after>
<aop:around method="around" pointcut-ref="p"></aop:around>
<aop:after-throwing method="afterThrow" pointcut-ref="p"></aop:after-throwing>
<aop:after-returning method="afterruning" pointcut-ref="p"></aop:after-returning>
</aop:aspect>
</aop:config>
代码实现
public void before(){
System.out.println("before ......");
}
public void after(){
System.out.println("after ......");
}
public void around(){
System.out.println("around ......");
}
public void afterruning(){
System.out.println("afterruning ......");
}
public void afterThrow(){
System.out.println("afterThrow ......");
}
4.4 测试类
@Test
public void testAopAnno(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}
@Test
public void testAopXml(){
ApplicationContext context =
new ClassPathXmlApplicationContext("bean2.xml");
Person person = context.getBean("person", Person.class);
person.add();
}
4.5 完全注解开发实现
config配置类
@Configuration /* 让当前类作为Spring的配置类 */
@ComponentScan(basePackages = "com.worker") /* 开启注解扫描 */
@EnableAspectJAutoProxy(proxyTargetClass = true) /* 开启AspectJ生成代理对象 为true 是选择使用什么动态代理方式 cglib代理或jdk代理*/
public class MyConfig {
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了