一、设计模式-代理模式
代理模式:给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理 就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不 能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。
客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的 其他角色代为创建并传入。
为什么要使用代理模式呢? 第一,它有间接的特点,可以起到中介隔离作用。 就好比在租房的时候,房东可能不在本地,而短期内又不能赶回来,此时中介的出场,就作为房东的代理实现和我们签订承租合同。而我们和房东之间就没有耦合了。
第二,它有增强的功能。还以租房为例,我们首先考虑的是找一个靠谱的中介,由中介 给我们提供房源信息,并且告诉我们房屋的细节,合同的细节等等。 当然我们也可以自己去 找一些个人出租的房屋,但是在这之中,我们要了解租赁合同,房屋验收,租金监管等情 况,这无疑对我们是繁重的工作。 而有了中介作为我们的代理中间人,他把了解到的信息告诉我们,我们一样可以租到房子,而不必做那些繁重的工作。
二、AOP思想及实现原理
1、AOP思想
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实现程序功能的统一维护的一种技术。 AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2、实现原理
在上面的概念中描述出aop的实现原理是基于动态代理技术实现的。下面是针对动态代 理的一些介绍: 特点: 字节码随用随创建,随用随加载 分类: 基于接口的动态代理,基于子类的动态代理 作用: 不修改源码的基础上对方法增强
(1)基于接口的动态代理:
提供者是:JDK官方 使用要求:被代理类最少实现一个接口。 涉及的类:Proxy 创建代理对象的方法:newProxyInstance
方法的参数: ClassLoader :类加载器。用于加载代理对象的字节码的。和被代理对象使用相同的类加载器。固定写法。 Class[] :字节码数组。用于给代理对象提供方法。和被代理对象具有相同的方法。
被代理类是一个普通类:被代理类对象.getClass().getInterfaces();
被代理类是一个接口:new Class[]{被代理了.class} 它也是固定写法InvocationHanlder:要增强的方法。此处是一个接口,我们需要提供它的实现类。通常写的是匿名内部类。增强的代码谁用谁写。
基于子类的动态代理
提供者是:第三方cglib包,在使用时需要先导包(maven工程导入坐标即可)
使用要求:被代理类不能是最终类,不能被final修饰
涉及的类:Enhancer
创建代理对象的方法:create
方法的参数:
Class:字节码。被代理对象的字节码。可以创建被代理对象的子类,还可以获取被代理对象的类加载器。 Callback:增强的代码。谁用谁写。通常都是写一个接口的实现类或者匿名内部类。
Callback中没有任何方法,所以我们一般使用它的子接口:MethodInterceptor
3、Spring中AOP的术语
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点): 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, 可以在运行期为类动态地添加一些方法或Field。
Target(目标对象): 代理的目标对象。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理): 一个类被AOP织入增强后,就产生一个结果代理类。
Aspect(切面): 是切入点和通知(引介)的结合。
三、Spring注解驱动AOP开发入门
1、写在最前
a.Spring的aop是基于ioc的。所以需要有spring的ioc基础。(本篇内容不对ioc进行讲解) b.本章节我们只是对aop的使用做基本功能展示,目的是为了以此讲解aop中的注解和执行原理分析。
2、注解驱动入门案例介绍
需求:实现在执行service方法时输出执行日志。(除了业务层外,表现层和持久层也可以实现)
3、案例实现
<?xml version="1.0" encoding="UTF-8"?>
<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>cn.itbluebox</groupId>
<artifactId>spring-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
</project>
- 创建User实体类
package cn.itbluebox.pojo;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private String id;
private String username;
private String password;
private String email;
private Date birthday;
private String gender;
private String mobile;
private String nickname;
public User() {
}
public User(String id, String username, String password, String email, Date birthday, String gender, String mobile, String nickname) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.birthday = birthday;
this.gender = gender;
this.mobile = mobile;
this.nickname = nickname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", birthday=" + birthday +
", gender='" + gender + '\'' +
", mobile='" + mobile + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
- 业务层接口
package cn.itbluebox.service;
import cn.itbluebox.pojo.User;
public interface UserService {
void save(User user);
}
- 业务层接口实现类
package cn.itbluebox.service.impl;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
public void save(User user) {
System.out.println("保存用户信息:"+user);
}
}
- 日志工具类
package cn.itbluebox.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogUtil {
@Pointcut("execution(* cn.itbluebox.service.impl.*.*(..))")
private void pt1(){}
@Before("pt1()")
public void beforeLog(){
System.out.println("执行切入点方法前记录日志");
}
@AfterReturning("pt1()")
public void afterReturningLog(){
System.out.println("正常执行切入点方法后记录日志");
}
@AfterReturning("pt1()")
public void afterThrowingLog(){
System.out.println("执行切入点方法产生异常后记录日志");
}
@After("pt1()")
public void afterLog(){
System.out.println("无论切入点方法执行是否异常都记录日志");
}
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
System.out.println("执行切入点方法前记录日志");
Object[] args = pjp.getArgs();
rtValue = pjp.proceed(args);
System.out.println("正常执行切入点方法后记录日志");
}catch (Throwable t){
System.out.println("执行切入点方法产生异常后记录日志"+t);
}finally {
System.out.println("无论切入点方法执行是否有异常都记录日志");
}
return rtValue;
}
}
- 创建配置类:config.SpringConfiguration
package cn.itbluebox.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("cn.itbluebox")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
- 创建测试类:
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean("userService", UserService.class);
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("张三");
userService.save(user);
}
}
运行测试类
4、可以将切入点精确到方法
- 在接口当中创建update方法
public void update(User user) {
System.out.println("保存用户信息:"+user);
}
- 修改LogUtil
@Pointcut("execution(* cn.itbluebox.service.impl.UserServiceImpl.save(..))")
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean("userService", UserService.class);
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("张三");
userService.update(user);
}
}
- 运行测试类(没有切入)
四、AOP常用注解分析
1、用于开启注解AOP支持
(1)@EnableAspectJAutoProxy
1)源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
2)说明
作用:
表示开启spring对注解aop的支持。
它有两个属性,分别是指定采用的代理方式和 是否暴露代理对象,通过AopContext可以进行访问。
从定义可以看得出,它引入AspectJAutoProxyRegister.class 对象,该对象是基于注解@EnableAspectJAutoProxy 注册一个AnnotationAwareAspectJAutoProxyCreator ,该对象通过调用
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
注册一个aop代理对象生成器。 关于AnnotationAwareAspectJAutoProxyCreator 请参
属性: proxyTargetClass :指定是否采用cglib进行代理。默认值是false,表示使用jdk的代理。 exposeProxy : 指定是否暴露代理对象,通过AopContext 可以进行访问。
使用场景: 当我们注解驱动开发时,在需要使用aop 实现某些功能的情况下,都需要用到此注解。
3)示例
package cn.itbluebox.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("cn.itbluebox")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
2、用于配置切面的
(1)@Aspect
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
public String value() default "";
}
2)说明
作用:声明当前类是一个切面类。
属性: value :默认我们的切面类应该为单例的。但是当切面类为一个多例类时,指定预处理的切入点表达式。用法是perthis(切入点表达式)。
它支持指定切入点表达式,或者是用@Pointcut 修饰的方法名称(要求全限定方法名)
使用场景 :此注解也是一个注解驱动开发aop的必备注解。
3)示例
@Component
@Scope("prototype")
@Aspect(value="execution(* cn.itbluebox.service.impl.*.*(..))")
public class LogUtil {
@Before("execution(* cn.itbluebox.service.impl.*.*(..))")
public void printLog(){
System.out.println("执行打印日志的功能");
}
}
3、用于配置切入点表达式的
(1)@Pointcut
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Pointcut {
String value() default "";
String argNames() default "";
}
2)说明
作用: 此注解是用于指定切入点表达式的。 属性: value:用于指定切入点表达式。
argNames :用于指定切入点表达式的参数。参数可以是execution中的,也可以是args中的。通常情况下不使用此属性也可以获得切入点方法参数。
使用场景 :在实际开发中,当我们的多个通知需要执行,同时增强的规则确定的情况下,就可以把切入点表达式通用化。此注解就是代替xml中的<aop:pointcut> 标签,实现切入点表达式的通用化。
3)示例
@Pointcut("execution(* cn.itbluebox.service.impl.UserServiceImpl.save(..))")
private void pt1(){}
4、用于配置通知的
(1)@Before
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
String value();
String argNames() default "";
}
2)说明
作用: 被此注解修饰的方法为前置通知。前置通知的执行时间点是在切入点方法执行之 前。 属性: value :用于指定切入点表达式。可以是表达式,也可以是表达式的引用。 argNames :用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。 使用场景:在实际开发中,我们需要对切入点方法执行之前进行增强, 此时就用到了前置通 知。 在通知(增强的方法)中需要获取切入点方法中的参数进行处理时,就要配合切入点表达 式参数来使用。
3)示例
@Before(value = "pt1(user)",argNames = "user")
public void beforeLog(User user){
System.out.println("执行切入点方法前记录日志"+user);
}
(2)@AfterReturning
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
String value() default "";
String pointcut() default "";
String returning() default "";
String argNames() default "";
}
2)说明
作用: 用于配置后置通知。 后置通知的执行是在切入点方法正常执行之后执行。 需要注意的是,由于基于注解的配置时,spring创建通知方法的拦截器链时,后置 通知在最终通知之后,所以会先执行@After 注解修饰的方法。
属性: value :用于指定切入点表达式,可以是表达式,也可以是表达式的引用。 pointcut :它的作用和value是一样的。 returning :指定切入点方法返回值的变量名称。它必须和切入点方法返回值名称一 致。 argNames :用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。 使用场景:此注解是用于配置后置增强切入点方法的。被此注解修饰方法会在切入点方法正常执行之后执行。 在我们实际开发中,像提交事务,记录访问日志,统计方法执行效率等等都可以利用后置通知实现。
User findById(String id);
public User findById(String id) {
System.out.println("切入点方法开始执行。。。");
User user = new User();
user.setId(id);
user.setUsername("itbluebox");
user.setNickname("蓝盒子");
return user;
}
@AfterReturning(value = "execution(* cn.itbluebox.service.impl.*.* (..))&&args(param)",
returning = "user")
public void afterReturningLog(String param,Object user){
System.out.println("-----------------------");
System.out.println("正常执行切入点方法后记录日志,切入点方法的参数 是:"+param);
System.out.println("正常执行切入点方法后记录日志,切入点方法的返回值 是:"+user);
System.out.println("-----------------------");
}
- 修改测试类
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean("userService", UserService.class);
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("张三");
userService.findById("1");
}
}
(3)@AfterThrowing
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {
String value() default "";
String pointcut() default "";
String throwing() default "";
String argNames() default "";
}
2)说明
作用: 用于配置异常通知。 属性:
value :用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
pointcut :它的作用和value是一样的。
throwing :指定切入点方法执行产生异常时的异常对象变量名称。它必须和异常变量名称一致。
argNames :用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。 通常不指定也可以获取切入点方法的参数内容。
使用场景 :用此注解修饰的方法执行时机是在切入点方法执行产生异常之后执行。
3)示例
package cn.itbluebox.service.impl;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
public void save(User user) {
System.out.println("保存用户信息:"+user);
}
public void update(User user) {
System.out.println("保存用户信息:"+user);
}
public User findById(String id) {
System.out.println("切入点方法开始执行。。。");
User user = new User();
user.setId(id);
user.setUsername("itbluebox");
user.setNickname("蓝盒子");
int i = 1/0;
return user;
}
}
(3)@After
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
String value();
String argNames() default "";
}
2)说明
作用: 用于指定最终通知。 属性: value :用于指定切入点表达式,可以是表达式,也可以是表达式的引用。 argNames :用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。 使用场景 :最终通知的执行时机,是在切入点方法执行完成之后执行,无论切入点方法执行是 否产生异常最终通知都会执行。所以被此注解修饰的方法,通常都是做一些清理操作。
3)示例
@After(value = "execution(* cn.itbluebox.service.impl.*.*(..))")
public void afterLog(){
System.out.println("无论切入点方法执行是否有异常都记录日志");
}
(4)@Around
1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Around {
String value();
String argNames() default "";
}
2)说明
作用:用于指定环绕通知。
属性: value:用于指定切入点表达式,可以是表达式,也可以是表达式的引用。 argNames:用于指定切入点表达式参数的名称。 它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:环绕通知有别于前面介绍的四种通知类型。它不是指定增强方法执行时机的,而是 spring为我们提供的一种可以通过编码的方式手动控制增强方法何时执行的机制。
3)示例
@Around("execution(* com.itheima.service.impl.*.*(..))")
public Object arountPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
System.out.println("执行切入点方法前记录日志");
Object[] args = pjp.getArgs();
rtValue = pjp.proceed(args);
System.out.println("正常执行切入点方法后记录日志");
}catch (Throwable t){
System.out.println("执行切入点方法产生异常后记录日志");
}finally {
System.out.println("无论切入点方法执行是否有异常都记录日志");
}
return rtValue;
}
五、用于扩展目标类的
1、@DeclareParents
(1)源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DeclareParents {
String value();
Class defaultImpl() default DeclareParents.class;
}
(2)说明
作用: 用于给被增强的类提供新的方法。(实现新的接口)
属性: value :用于指定目标类型的表达式。当在全限定类名后面跟上+时,表示当前类及其子类
defaultImpl :指定提供方法或者字段的默认实现类。
使用场景 :当我们已经完成了一个项目的某个阶段开发,此时需要对已完成的某个类加入一些新的方法,我们首先想到的是写一个接口,然后让这些需要方法的类实现此接口,但是如果目标类非常复杂,牵一发而动全身,改动的话可能非常麻烦。 此时就可以使用此注解,然后建一个代理类,同时代理该类和目标类。
(3)示例
package cn.itbluebox.service;
import cn.itbluebox.pojo.User;
public interface ValidateService {
boolean checkUser(User user);
}
package cn.itbluebox.service.impl;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.ValidateService;
public class ValidateServiceImpl implements ValidateService {
@Override
public boolean checkUser(User user) {
if(user.getNickname().contains("孙子")){
return false;
}
return true;
}
}
- 创建MyPointcut
package cn.itbluebox.pointcut;
import org.aspectj.lang.annotation.Pointcut;
public class MyPointcut {
@Pointcut(value = "execution(* cn.itbluebox.service.impl.*.*(..))")
public void pointcut1(){}
}
package cn.itbluebox.utils;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.ValidateService;
import cn.itbluebox.service.impl.ValidateServiceImpl;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogUtil {
@DeclareParents(value =
"cn.itbluebox.service.UserService+", defaultImpl =
ValidateServiceImpl.class)
private ValidateService validateService;
@Before(value = "cn.itbluebox.pointcut.MyPointcut.pointcut1() && args(user) && this(validateService)")
public void printLog(User user, ValidateService validateService) {
boolean check = validateService.checkUser(user);
if (check) {
System.out.println("执行打印日志的功能");
} else {
throw new IllegalStateException("名称非法");
}
}
@Pointcut("execution(* cn.itbluebox.service.impl.*.*(..))")
private void pt1() {
}
@Before("pt1()")
public void beforeLog() {
System.out.println("执行切入点方法前记录日志");
}
@AfterReturning(value = "execution(* cn.itbluebox.service.impl.*.* (..))&&args(param)",
returning = "user")
public void afterReturningLog(String param, Object user) {
System.out.println("-----------------------");
System.out.println("正常执行切入点方法后记录日志,切入点方法的参数 是:" + param);
System.out.println("正常执行切入点方法后记录日志,切入点方法的返回值 是:" + user);
System.out.println("-----------------------");
}
@AfterReturning("pt1()")
public void afterThrowingLog() {
System.out.println("执行切入点方法产生异常后记录日志");
}
@After("pt1()")
public void afterLog() {
System.out.println("无论切入点方法执行是否异常都记录日志");
}
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
System.out.println("执行切入点方法前记录日志");
Object[] args = pjp.getArgs();
rtValue = pjp.proceed(args);
System.out.println("正常执行切入点方法后记录日志");
} catch (Throwable t) {
System.out.println("执行切入点方法产生异常后记录日志" + t);
} finally {
System.out.println("无论切入点方法执行是否有异常都记录日志");
}
return rtValue;
}
}
package cn.itbluebox.test;
import cn.itbluebox.config.SpringConfiguration;
import cn.itbluebox.pojo.User;
import cn.itbluebox.service.UserService;
import cn.itbluebox.service.ValidateService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAOPTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = ac.getBean("userService", UserService.class);
User user = new User();
user.setId("1");
user.setUsername("test");
user.setNickname("孙子");
userService.save(user);
}
}
2、@EnableLoadTimeWeaving
(1)源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(LoadTimeWeavingConfiguration.class)
public @interface EnableLoadTimeWeaving {
AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT;
enum AspectJWeaving {
ENABLED,
DISABLED,
AUTODETECT;
}
}
(2)说明
作用: 用于切换不同场景下实现增强。 属性: aspectjWeaving :是否开启LTW的支持。 ENABLED 开启LTW DISABLED 不开启LTW AUTODETECT 如果类路径下能读取到META‐INF/aop.xml 文件,则开启LTW,否则关闭 使用场景:在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。
编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。
AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)
(3)示例
**
* @author 黑马程序员
* @Company http://www.itheima.com
*/
@Aspect
public class LoadTimeWeavingAspect {
@Around("pointcut()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch(getClass().getSimpleName());
try {
sw.start(pjp.getSignature().getName());
return pjp.proceed();
} finally {
sw.stop();
System.out.println(sw.prettyPrint());
}
}
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
public void pointcut() {
}
}
|