P101 GoF代理模式之JDK动态代理工具类封装
P101 start :
ProxyUtil类中的内容为:
package com.powernode.proxy.util;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyUtil {
/**
* 封装一个工具方法,可以通过这个方法获取代理对象
* @param target
* @return
*/
public static Object newProxyInstance(Object target){
//底层是调用的还是JDK的动态代理。
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
}
}
Client客户端类中的内容为:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;
import com.powernode.proxy.util.ProxyUtil;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
代理类和目标类要实现同一个接口或同一些接口。
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
第三个参数:InvocationHandler h
InvocationHandler 被翻译为:调用处理器。是一个接口。
在调用处理器接口中编写的就是:增强代码。
因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
既然是接口,就要写接口的实现类。
可能会有疑问?
自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
因为这种调用处理器写一次就好。
注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
*/
// OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
// new TimerInvocationHandler(target));
//上面代码通过一个工具类的封装,就简洁了。
OrderService proxyObj = (OrderService) ProxyUtil.newProxyInstance(target);
//调用代理对象的代理方法
//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
proxyObj.generate();
proxyObj.modify();
proxyObj.detail();
String name = proxyObj.getName();
System.out.println(name);
}
}
P101 end
P102 GoF代理模式之CGLIB动态代理
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现,所以被代理的目标不能使用final修饰,使用CGLIB需要引入它的依赖。
CGLIB依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
UserServicel类中的的内容:
package com.powernode.proxy.service;
public class UserService {
//目标方法
public boolean login(String username, String password){
System.out.println("系统正在验证身份...");
if ("admin".equals(username)&&"123".equals(password)) {
return true;
}
return false;
}
//目标方法
public void logout(){
System.out.println("系统正在退出...");
}
}
TimerMethodInterceptor类中的内容:
package com.powernode.proxy.service;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodeInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//签名增强
long begin = System.currentTimeMillis();
//怎么调用目标对象的目标方法呢?
Object retValue = methodProxy.invokeSuper(target, objects);
//后面增强
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
return retValue;
}
}
Client类中的内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.TimerMethodeInterceptor;
import com.powernode.proxy.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
//创建字节码增强器对象
//这个对象是CGLIB库当中的核心对象,就是依靠它来代理类。
Enhancer enhancer = new Enhancer();
//告诉CGLIB父类是谁。告诉CGLIB目标类是谁。
enhancer.setSuperclass(UserService.class);
//设置回调(等同于JDK动态代理当中的调用处理球,InvocationHandler)
//在CGLIB当中不是InvocationHandler接口,是方法拦截器接口:MethodInterceptor
enhancer.setCallback(new TimerMethodeInterceptor());
//创建代理对象
//这一步会做两件事:
//第一件事:在内存中生成UserService类的子类,其实就是代理类的字节码。
//第二件事:创建代理对象。
//父类是UserService,子类这个代理类一定是UserService
UserService userServiceProxy = (UserService) enhancer.create();
//建议大家能够把CGLIB动态代理生成的代理对象的名字格式有点印象
//根据这个名字可以推测框架底层是否使用了CGLIB动态代理
System.out.println(userServiceProxy);
//调用代理对象的代理方法。
boolean success = userServiceProxy.login("admin", "123");
System.out.println(success ? "登录成功": "登录失败");
userServiceProxy.logout();
}
//底层本质:
//com.powernode.proxy.service.UserService$$EnhancerByCGLIB$$82cb55e3@5d3411d
}
运行结果:
耗时0毫秒
耗时15毫秒
com.powernode.proxy.service.UserService$$EnhancerByCGLIB$$82cb55e3@5d3411d
系统正在验证身份...
耗时2毫秒
登录成功
系统正在退出...
耗时0毫秒
P103 面向切面编程之面向切面编程的深入理解 start:
十五、面向切面编程AOP
IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能,把它转化成组件。
AOP(Aspect Oriented Programming):面向切面编程,面向方面编程。(AOP是一种编程技术)
AOP是对OOP的补充延伸。
AOP底层使用的就是动态代理来实现的。
Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果要代理某个类,这个类没有实现接口,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。
15.1 AOP介绍
一般一个系统当中都会有一些系统服务,例如:日志、事务管理、安全等。这些系统服务被称为:交叉业务
这些交叉业务几乎是通用的,不管你是做银行账户转账,还是删除用户数据。日志、事务管理、安全,这些都是需要做的。
如果在每一个业务处理过程当中,都掺杂这些交叉业务代码进去的话,存在两方面问题:
● 第一:交叉业务代码在多个业务流程中反复出现,显然这个交叉业务代码没有得到复用。并且修改这些交叉业务代码的话,需要修改多处。
● 第二:程序员无法专注核心业务代码的编写,在编写核心业务代码的同时还需要处理这些交叉业务。
使用AOP可以很轻松的解决以上问题。
用一句话总结AOP:将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
AOP的优点:
● 第一:代码复用性增强。
● 第二:代码易维护。
● 第三:使开发者更关注业务逻辑
P104 面向切面编程之七大术语 start:
● 连接点 Joinpoint
○ 在程序的整个执行流程中,可以织入切面的位置。方法的执行前后,异常抛出之后等位置。
● 切点 Pointcut
○ 在程序执行流程中,真正织入切面的方法。(一个切点对应多个连接点)
● 通知 Advice
○ 通知又叫增强,就是具体你要织入的代码。
○ 通知包括:
■ 前置通知
■ 后置通知
■ 环绕通知
■ 异常通知
■ 最终通知
● 切面 Aspect
○ 切点 + 通知就是切面。
● 织入 Weaving
○ 把通知应用到目标对象上的过程。
● 代理对象 Proxy
○ 一个目标对象被织入通知后产生的新对象。
● 目标对象 Target
○ 被织入通知的对象。
P105 面向切面编程之切点表达式 start:
15.3 切点表达式
切点表达式用来定义通知(Advice)往哪些方法上切入。
切入点表达式语法格式:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
访问控制权限修饰符:
● 可选项。
● 没写,就是4个权限都包括。
● 写public就表示只包括公开的方法。
返回值类型:
● 必填项。
● * 表示返回值类型任意。
全限定类名:
● 可选项。
● 两个点“..”代表当前包以及子包下的所有类。
● 省略时表示所有的类。
方法名:
● 必填项。
● 表示所有方法。● set表示所有的set方法。
形式参数列表:
● 必填项
● () 表示没有参数的方法
● (..) 参数类型和个数随意的方法
● () 只有一个参数的方法● (, String) 第一个参数类型随意,第二个参数是String的。
异常:
● 可选项。
● 省略时表示任意异常类型。
P106 Spirng AOP实现之概述 start:
Spring对AOP的实现包括以下3种方式:
● 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
● 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
● 第三种方式:Spring框架自己实现的AOP,基于XML配置方式。
实际开发中,都是Spring+AspectJ来实现AOP。所以我们重点学习第一种和第二种方式。
什么是AspectJ?(Eclipse组织的一个支持AOP的框架。AspectJ框架是独立于Spring框架之外的一个框架,Spring框架用了AspectJ)
AspectJ项目起源于帕洛阿尔托(Palo Alto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregor Kiczales领导,从1997年开始致力于AspectJ的开发,1998年第一次发布给外部用户,2001年发布1.0 release。为了推动AspectJ技术和社团的发展,PARC在2003年3月正式将AspectJ项目移交给了Eclipse组织,因为AspectJ的发展和受关注程度大大超出了PARC的预期,他们已经无力继续维持它的发展。
15.4.1 准备工作
使用Spring+AspectJ的AOP需要引入的依赖如下:
<!--spring context依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
Spring配置文件中添加context命名空间和aop命名空间
<?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">
</beans>
107 AOP基于注解之准备工作 start:在上面!~
P108 Spring AOP基于注解之实现步骤 start:
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
@Before("execution(* com.powernode.spring6.service.UserService.*(..) )")
public void 增强(){
System.out.println("我是一个通知,我是一段增强代码。。。。");
}
}
UserService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {//目标类
public void login(){ //目标方法
System.out.println("系统正在进行身份认证。。。。");
}
}
spring配置文件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">
<!--组件扫描 -->
<context:component-scan base-package="com.powernode.spring6.service"/>
<!--开启aspectj的自动代理 -->
<!--spring容器在扫描类的时候,查看该类上是否有@Aspect注解,如果有,则给这个类生成代理对象. -->
<!--
proxy-target-class="true" 表示强制使用CGLIB代理
proxy-target-class="false" 这是默认值,表示接口使用JDK动态代理,反之使用CGLIB代理。
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAOPTest {
@Test
public void testBefore(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.login();
}
}
运行结果:
我是一个通知,我是一段增强代码。。。。
系统正在进行身份认证。。。。
P109 Spring AOP基于注解之切面表达式 Start:
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
@Before("execution(* com.powernode.spring6.service..*(..) )")
public void 增强(){
System.out.println("我是一个通知,我是一段增强代码。。。。");
}
}
OrderService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("orderService")
public class OrderService {//目标类
//目标方法
public void generate(){
System.out.println("生成订单");
}
}
UserService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {//目标类
public void login(){ //目标方法
System.out.println("系统正在进行身份认证。。。。");
}
public void logout(){
System.out.println("退出系统。。。");
}
}
spring类中的内容:
<?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">
<!--组件扫描 -->
<context:component-scan base-package="com.powernode.spring6.service"/>
<!--开启aspectj的自动代理 -->
<!--spring容器在扫描类的时候,查看该类上是否有@Aspect注解,如果有,则给这个类生成代理对象. -->
<!--
proxy-target-class="true" 表示强制使用CGLIB代理
proxy-target-class="false" 这是默认值,表示接口使用JDK动态代理,反之使用CGLIB代理。
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.spring6.service.OrderService;
import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAOPTest {
@Test
public void testBefore(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.login();
userService.logout();
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
}
}
运行结果:
我是一个通知,我是一段增强代码。。。。
系统正在进行身份认证。。。。
我是一个通知,我是一段增强代码。。。。
退出系统。。。
我是一个通知,我是一段增强代码。。。。
生成订单
P110 Spring AOP基于注解之所有通知类型 start :
通知类型
通知类型包括:
● 前置通知:@Before 目标方法执行之前的通知
● 后置通知:@AfterReturning 目标方法执行之后的通知
● 环绕通知:@Around 目标方法之前添加通知,同时目标方法执行之后添加通知。
● 异常通知:@AfterThrowing 发生异常之后执行的通知
● 最终通知:@After 放在finally语句块中的通知
环绕的通知是最大的范围,在前置和后置的前面和后面
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
// @Before("execution(* com.powernode.spring6.service..*(..) )")
// public void 增强(){
// System.out.println("我是一个通知,我是一段增强代码。。。。");
// }
//前置通知
@Before("execution(* com.powernode.spring6.service..*(..) )")
public void beforeAdvice(){
System.out.println("前置通知");
}
//后置通知
@AfterReturning("execution(* com.powernode.spring6.service..*(..) )")
public void afterReturningAdvice(){
System.out.println("后置通知");
}
//环绕通知(环绕是最大的通知,在前置通知之前,在后置通知之后。)
@Around("execution(* com.powernode.spring6.service..*(..) )")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
//前面的代码
System.out.println("前环绕");
//执行目标
joinPoint.proceed();//执行目标
//后面的代码
System.out.println("后环绕");
}
//异常通知
@AfterThrowing("execution(* com.powernode.spring6.service..*(..) )")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//最终通知(finally语句块中的通知)
@After("execution(* com.powernode.spring6.service..*(..) )")
public void afterAdvice(){
System.out.println("最终通知");
}
}
OrderService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("orderService")
public class OrderService {//目标类
//目标方法
public void generate(){
System.out.println("系统正在生成订单。。。");
// if (1 == 1) {
// throw new RuntimeException("运行时异常。。。");
// }
}
}
UserService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {//目标类
public void login(){ //目标方法
System.out.println("系统正在进行身份认证。。。。");
}
public void logout(){
System.out.println("退出系统。。。");
}
}
spring配置文件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">
<!--组件扫描 -->
<context:component-scan base-package="com.powernode.spring6.service"/>
<!--开启aspectj的自动代理 -->
<!--spring容器在扫描类的时候,查看该类上是否有@Aspect注解,如果有,则给这个类生成代理对象. -->
<!--
proxy-target-class="true" 表示强制使用CGLIB代理
proxy-target-class="false" 这是默认值,表示接口使用JDK动态代理,反之使用CGLIB代理。
-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.spring6.service.OrderService;
import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAOPTest {
@Test
public void testBefore(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// UserService userService = applicationContext.getBean("userService", UserService.class);
// userService.login();
// userService.logout();
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
}
}
运行结果:
前环绕
前置通知
系统正在生成订单。。。
后置通知
最终通知
后环绕
P111 spring AOP基于注解之切面顺序 start:
@Order()注解,括号里谁的数字最小,谁的优先级最高
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
@Order(2)
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
// @Before("execution(* com.powernode.spring6.service..*(..) )")
// public void 增强(){
// System.out.println("我是一个通知,我是一段增强代码。。。。");
// }
//前置通知
@Before("execution(* com.powernode.spring6.service..*(..) )")
public void beforeAdvice(){
System.out.println("前置通知");
}
//后置通知
@AfterReturning("execution(* com.powernode.spring6.service..*(..) )")
public void afterReturningAdvice(){
System.out.println("后置通知");
}
//环绕通知(环绕是最大的通知,在前置通知之前,在后置通知之后。)
@Around("execution(* com.powernode.spring6.service..*(..) )")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
//前面的代码
System.out.println("前环绕");
//执行目标
joinPoint.proceed();//执行目标
//后面的代码
System.out.println("后环绕");
}
//异常通知
@AfterThrowing("execution(* com.powernode.spring6.service..*(..) )")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//最终通知(finally语句块中的通知)
@After("execution(* com.powernode.spring6.service..*(..) )")
public void afterAdvice(){
System.out.println("最终通知");
}
}
OrderService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("orderService")
public class OrderService {//目标类
//目标方法
public void generate(){
System.out.println("系统正在生成订单。。。");
// if (1 == 1) {
// throw new RuntimeException("运行时异常。。。");
// }
}
}
SecurityAspect类中的内容:
package com.powernode.spring6.service;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(3)
public class SecurityAspect {//安全切面
//通知 + 切点
@Before("execution(* com.powernode.spring6.service..*(..) )")
public void beforeAdvice(){
System.out.println("前置通知:安全。。。");
}
}
UserService类中的内容:
package com.powernode.spring6.service;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserService {//目标类
public void login(){ //目标方法
System.out.println("系统正在进行身份认证。。。。");
}
public void logout(){
System.out.println("退出系统。。。");
}
}
spring配置文件与上集相同
测试类中的内容:
package com.powernode.spring6.test;
import com.powernode.spring6.service.OrderService;
import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAOPTest {
@Test
public void testBefore(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// UserService userService = applicationContext.getBean("userService", UserService.class);
// userService.login();
// userService.logout();
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService.generate();
}
}
运行结果:
前环绕
前置通知
前置通知:安全。。。
系统正在生成订单。。。
后置通知
最终通知
后环绕
P112 spring AOP基于注解之通用切点 start:
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
@Order(2)
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
// @Before("execution(* com.powernode.spring6.service..*(..) )")
// public void 增强(){
// System.out.println("我是一个通知,我是一段增强代码。。。。");
// }
//定义通用的切点表达式
@Pointcut("execution(* com.powernode.spring6.service..*(..) )")
public void 通用切点(){
//这个方法只是一个标记,方法名随意,方法体中也不需要写任何代码
}
//前置通知
@Before("通用切点()")
public void beforeAdvice(){
System.out.println("前置通知");
}
//后置通知
@AfterReturning("通用切点()")
public void afterReturningAdvice(){
System.out.println("后置通知");
}
//环绕通知(环绕是最大的通知,在前置通知之前,在后置通知之后。)
@Around("通用切点()")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
//前面的代码
System.out.println("前环绕");
//执行目标
joinPoint.proceed();//执行目标
//后面的代码
System.out.println("后环绕");
}
//异常通知
@AfterThrowing("通用切点()")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//最终通知(finally语句块中的通知)
@After("通用切点()")
public void afterAdvice(){
System.out.println("最终通知");
}
}
SecerityAspect类中的内容:
package com.powernode.spring6.service;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(3)
public class SecurityAspect {//安全切面
//通知 + 切点
// @Before("execution(* com.powernode.spring6.service..*(..) )")
@Before("com.powernode.spring6.service.LogAspect.通用切点()")
public void beforeAdvice(){
System.out.println("前置通知:安全。。。");
}
}
测试类与上集相同,spring配置文件与上集相同
运行结果:
前环绕
前置通知
前置通知:安全。。。
系统正在生成订单。。。
后置通知
最终通知
后环绕
P113 spring AOP基于注解之连接点 start:
LogAspect类中的内容:
package com.powernode.spring6.service;
import jdk.swing.interop.SwingInterOpUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component("logAspect")
@Aspect //切面类是需要使用@Aspect注解进行标注的。
@Order(2)
public class LogAspect { //切面
//切面 = 通知 + 切点
//通知就是增强,就是具体的要编写的增强代码
//这里通知Advice以方法的形式出现。(因为方法中可以写代码)
//@Before注解标注的方法就是一个前置通知。
//括号中要写切点表达式
//@Before("execution(修饰符 返回值类型 全限定类名 方法名(形式参数列表) 异常)")
// @Before("execution(* com.powernode.spring6.service..*(..) )")
// public void 增强(){
// System.out.println("我是一个通知,我是一段增强代码。。。。");
// }
//定义通用的切点表达式
@Pointcut("execution(* com.powernode.spring6.service..*(..) )")
public void 通用切点(){
//这个方法只是一个标记,方法名随意,方法体中也不需要写任何代码
}
//前置通知
@Before("通用切点()")
public void beforeAdvice(JoinPoint joinPoint){
System.out.println("前置通知");
//这个JoinPoint joinPoint,在spring容器调用这个方法的时候自动传过来
//我们可以直接用,用这个JoinPoint joinPoint 干啥?
// Signature signature = joinPoint.getSignature(); //获取目标方法的签名
//Signature signature = joinPoint.getSignature();
//通过方法的前面可以获取到一个方法的具体信息。
//获取目标方法的方法名
System.out.println("目标方法的方法名"+joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning("通用切点()")
public void afterReturningAdvice(){
System.out.println("后置通知");
}
//环绕通知(环绕是最大的通知,在前置通知之前,在后置通知之后。)
@Around("通用切点()")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
//前面的代码
System.out.println("前环绕");
//执行目标
joinPoint.proceed();//执行目标
//后面的代码
System.out.println("后环绕");
}
//异常通知
@AfterThrowing("通用切点()")
public void afterThrowingAdvice(){
System.out.println("异常通知");
}
//最终通知(finally语句块中的通知)
@After("通用切点()")
public void afterAdvice(){
System.out.println("最终通知");
}
}
其他文件与上集相同
运行结果:
前环绕
前置通知
目标方法的方法名generate
前置通知:安全。。。
系统正在生成订单。。。
后置通知
最终通知
后环绕
P114 spring AOP基于注解之全注解开发 start :
Time:4h
视频网址:
https://www.bilibili.com/video/BV1Ft4y1g7Fb?p=113&vd_source=ff898217e3612fcbdefecfdc49657ab1