SpringIOC以及AOP注解开发
和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是 Java 代码来完成的,XML 和注解只是告诉框架中的 Java 代码如何执行。
新建一个maven项目
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>spring-aop</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-aop</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <!--这里是设置作用域--> <!--注释作用域,表明我想让它为全局使用--> <!--<scope>provided</scope>--> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies> </project>
注解开发的话当然需要配置类
@EnableAspectJAutoProxy
package lps.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * @author 阿水 * @create 2023-02-23 14:52 */ @Configuration @ComponentScan({"lps"}) @EnableAspectJAutoProxy public class SpringConfig { }
①使用@Component注解标记的普通组
②使用@Controller注解标记的控制器组件
③使用@Service注解标记的业务逻辑组件
④使用@Repository注解标记的持久化层组件
通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别,也就是语法层面没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
@Autowired和@Resource区别
提供方不同
@Autowired 是Spring提供的,@Resource 是J2EE提供的。
装配时默认类型不同
@Autowired只按type装配,@Resource默认是按name装配。
切入点表达式的写法-execution函数
关键字:execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名...类名.方法名
规则 | 表达式 | 示例 |
---|---|---|
匹配所有类的public方法 | execution(public **(..)) | |
匹配指定包下所有类的方法(不包含子包) | execution(* 包名.*(..)) | execution(* test.aop.t1.*(..)) |
匹配指定包下所有类的方法(包含子包) | execution(* 包名.**(..)) | execution(* test.aop.t1..*(..)) |
匹配指定类下的所有方法 | execution(* 包名.类名.*(..)) | execution(* test.aop.t1.UserDao.*(..)) |
匹配指定类下的指定方法 | execution(* 包名.类名.方法名(..)) | execution(* test.aop.t1.UserDao.add(..)) |
匹配实现指定接口的所有类的方法 | execution(* 包名.接口名+*(..)) | execution(* test.aop.t1.UserDao+*(..)) |
匹配所有名称以save开头的方法 | execution(* save*(..)) |
Spring中的通知
Spring中一共有5种通知
前置通知(before)、后置通知(returning)、异常通知(throwing)、最终通知(after)、环绕通知(around)
复制代码
通知类型 | 作用 | 应用场景 |
---|---|---|
前置通知 | 在目标方法执行前增强代码逻辑 | 权限控制(权限不足抛出异常)、记录方法调用信息日志 |
后置通知 | 在目标方法运行后返回值后再增强代码逻辑,在出现异常时不执行 | 与业务相关的,如银行在存取款结束后的发送短信消息 */ |
异常通知 | 目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件) | 处理异常(一般不可预知),记录日志 |
最终通知 | 不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能) | 释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 ) |
环绕通知 | 目标方法执行前后,都进行增强(控制目标方法执行) | 日志、缓存、权限、性能监控、事务管理 |
UserService.java
package lps.service; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; /** * @author 阿水 * @create 2023-02-23 14:50 */ @Service("userService") public class UserService { /* public void sing(){ System.out.println("我爱唱歌"); } public void dance(){ System.out.println("我爱跳舞"); } public void rap() { //int i=10/0; System.out.println("我爱rap 篮球 起~"); }*/ public void doSum(){ long sum=0; for (long i = 0; i < 1000000000; i++) { sum+=i; } System.out.println(sum); } }
Aspect代码块
package lps.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.Date; /** * @author 阿水 * @create 2023-02-23 15:18 */ @Component @Aspect public class LoggerAspect { //@Pointcut("execution(public * lps.service.*.*(..))") @Pointcut("execution(* lps.*.*.*(..))") private void pointcut() { } /* @AfterReturning("pointcut()") public void after() { System.out.println("AfterReturning————————————————————你的鸡哥————————————————————"); } @Before("pointcut()") public void before() { System.out.println("Before________________________你的坤坤________________________"); } *//* @Around("pointcut()") public void around(){ System.out.println("_+++++环绕的鸡哥_+++++"); }*//* @AfterThrowing("pointcut()") public void logAfterThrowing(){ System.out.println("AfterThrowing出错啦 赶紧回来"); } @After("pointcut()") public void afterFinalyy() { System.out.println("After最终通知 "); }*/ @Around("pointcut()") public Object test(ProceedingJoinPoint point){ Object result=null; //long beginTime = System.currentTimeMillis(); long beginTime = new Date().getTime(); Object[] args = point.getArgs(); try { result = point.proceed(args); } catch (Throwable e) { throw new RuntimeException(e); } //long endTime = System.currentTimeMillis(); long endTime = new Date().getTime(); System.out.println("一起用了"+(endTime-beginTime)+"毫秒"); return result; } }
测试类
package lps.demo; import javafx.application.Application; import lps.config.SpringConfig; import lps.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author 阿水 * @create 2023-02-23 14:53 */ public class test1 { public static void main(String[] args) { ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = (UserService) context.getBean("userService"); /* userService.sing(); userService.dance(); userService.rap();*/ userService.doSum(); } }